[C++] 纯文本查看 复制代码
int SoftModalMessageBox(LPMSGBOXDATA lpmb) {
LPBYTE lpDlgTmp;
int cyIcon, cxIcon;
int cxButtons;
int cxMBMax;
int cxText, cyText, xText;
int cxBox, cyBox;
int cxFoo, cxCaption;
int xMB, yMB;
HDC hdc;
DWORD wIconOrdNum;
DWORD wCaptionLen;
DWORD wTextLen;
WORD OrdNum[2]; // Must be an array or WORDs
RECT rc;
RECT rcWork;
HCURSOR hcurOld;
DWORD dwStyleMsg, dwStyleText;
DWORD dwStyleDlg;
HWND hwndOwner;
LPWSTR lpsz;
int iRetVal = 0;
HICON hIcon;
HGLOBAL hTemplate = NULL;
HGLOBAL hCaption = NULL;
HGLOBAL hText = NULL;
HINSTANCE hInstMsg = lpmb->hInstance;
SIZE size;
HFONT hFontOld = NULL;
dwStyleMsg = lpmb->dwStyle;
if (!HIWORD(lpmb->lpszCaption)) {
// won't ever be NULL because MessageBox sticks "Error!" in in that case
if (hInstMsg && (hCaption = LocalAlloc(LPTR, MAX_RES_STRING * sizeof(WCHAR)))) {
lpsz = (LPWSTR) hCaption;
LoadString(hInstMsg, LOWORD(lpmb->lpszCaption), lpsz, MAX_RES_STRING);
} else
lpsz = NULL;
lpmb->lpszCaption = lpsz ? lpsz : szEmpty;
}
if (!HIWORD(lpmb->lpszText)) {
// NULL not allowed
if (hInstMsg && (hText = LocalAlloc(LPTR, MAX_RES_STRING * sizeof(WCHAR)))) {
lpsz = (LPWSTR) hText;
LoadString(hInstMsg, LOWORD(lpmb->lpszText), lpsz, MAX_RES_STRING);
} else
lpsz = NULL;
lpmb->lpszText = lpsz ? lpsz : szEmpty;
}
if ((dwStyleMsg & MB_ICONMASK) == MB_USERICON)
hIcon = LoadIcon(hInstMsg, lpmb->lpszIcon);
else
hIcon = NULL;
// For compatibility reasons, we still allow the message box to come up.
hwndOwner = lpmb->hwndOwner;
// For PowerBuilder4.0, we must make their messageboxes owned popups. Or, else
// they get WM_ACTIVATEAPP and they install multiple keyboard hooks and get into
// infinite loop later.
// Bug #15896 -- WIN95B -- 2/17/95 -- SANKAR --
if(!hwndOwner)
{
WCHAR pwszLibFileName[MAX_PATH];
static WCHAR szPB040[] = L"PB040"; // Module name of PowerBuilder4.0
WCHAR *pw1;
//Is this a win3.1 or older app?
if(GetClientInfo()->dwExpWinVer <= VER31)
{
if (GetModuleFileName(NULL, pwszLibFileName, sizeof(pwszLibFileName)/sizeof(WCHAR)) == 0) goto getthedc;
pw1 = pwszLibFileName + wcslen(pwszLibFileName) - 1;
while (pw1 > pwszLibFileName) {
if (*pw1 == TEXT('.')) *pw1-- = 0;
else if (*pw1 == TEXT(':')) {pw1++; break;}
else if (*pw1 == TEXT('\\')) {pw1++; break;}
else pw1--;
}
// Is this the PowerBuilder 4.0 module?
if(!_wcsicmp(pw1, szPB040))
hwndOwner = NtUserGetForegroundWindow(); // Make the MsgBox owned.
}
}
getthedc:
// Check if we're out of cache DCs until robustness...
if (!(hdc = NtUserGetDCEx(NULL, NULL, DCX_WINDOW | DCX_CACHE)))
goto SMB_Exit;
// Figure out the types and dimensions of buttons
cxButtons = (lpmb->cButtons * gpsi->wMaxBtnSize) + ((lpmb->cButtons - 1) * XPixFromXDU(DU_BTNGAP, gpsi->cxMsgFontChar));
// Ditto for the icon, if there is one. If not, cxIcon & cyIcon are 0.
if (wIconOrdNum = MB_GetIconOrdNum(dwStyleMsg)) {
cxIcon = SYSMET(CXICON) + XPixFromXDU(DU_INNERMARGIN, gpsi->cxMsgFontChar);
cyIcon = SYSMET(CYICON);
} else
cxIcon = cyIcon = 0;
hFontOld = SelectObject(hdc, gpsi->hCaptionFont);
// Find the max between the caption text and the buttons
wCaptionLen = wcslen(lpmb->lpszCaption);
GetTextExtentPoint(hdc, lpmb->lpszCaption, wCaptionLen, &size);
cxCaption = size.cx + 2*SYSMET(CXSIZE);
//
// The max width of the message box is 5/8 of the work area for most
// countries. We will then try 6/8 and 7/8 if it won't fit. Then
// we will use whole screen.
//
CopyRect(&rcWork, &gpsi->rcWork);
cxMBMax = MultDiv(rcWork.right - rcWork.left, 5, 8);
cxFoo = 2*XPixFromXDU(DU_OUTERMARGIN, gpsi->cxMsgFontChar);
SelectObject(hdc, gpsi->hMsgFont);
//
// If the text doesn't fit in 5/8, try 7/8 of the screen
//
//#if defined(JAPAN) || defined(KOREA)
ReSize:
//#endif
//
// The message box is as big as needed to hold the caption/text/buttons,
// but not bigger than the maximum width.
//
cxBox = cxMBMax - 2*SYSMET(CXFIXEDFRAME);
/* Ask DrawText for the right cx and cy */
rc.left = 0;
rc.top = 0;
rc.right = cxBox - cxFoo - cxIcon;
rc.bottom = rcWork.bottom - rcWork.top;
cyText = DrawTextExW(hdc, (LPWSTR)lpmb->lpszText, -1, &rc,
DT_CALCRECT | DT_WORDBREAK | DT_EXPANDTABS |
DT_NOPREFIX | DT_EXTERNALLEADING | DT_EDITCONTROL, NULL);
//
// Make sure we have enough width to hold the buttons, in addition to
// the icon+text. Always force the buttons. If they don't fit, it's
// because the working area is small.
//
//
// The buttons are centered underneath the icon/text.
//
cxText = rc.right - rc.left + cxIcon + cxFoo;
cxBox = min(cxBox, max(cxText, cxCaption));
cxBox = max(cxBox, cxButtons + cxFoo);
cxText = cxBox - cxFoo - cxIcon;
//
// Now we know the text width for sure. Really calculate how high the
// text will be.
//
rc.left = 0;
rc.top = 0;
rc.right = cxText;
rc.bottom = rcWork.bottom - rcWork.top;
cyText = DrawTextExW(hdc, (LPWSTR)lpmb->lpszText, -1, &rc, DT_CALCRECT | DT_WORDBREAK
| DT_EXPANDTABS | DT_NOPREFIX | DT_EXTERNALLEADING | DT_EDITCONTROL, NULL);
// Find the window size.
cxBox += 2*SYSMET(CXFIXEDFRAME);
cyBox = 2*SYSMET(CYFIXEDFRAME) + SYSMET(CYCAPTION) + YPixFromYDU(2*DU_OUTERMARGIN +
DU_INNERMARGIN + DU_BTNHEIGHT, gpsi->cyMsgFontChar);
cyBox += max(cyIcon, cyText);
//
// If the message box doesn't fit on the working area, we'll try wider
// sizes successively: 6/8 of work then 7/8 of screen.
//
if (cyBox > rcWork.bottom - rcWork.top)
{
int cxTemp;
cxTemp = MultDiv(rcWork.right - rcWork.left, 6, 8);
if (cxMBMax == MultDiv(rcWork.right - rcWork.left, 5, 8))
{
cxMBMax = cxTemp;
goto ReSize;
}
else if (cxMBMax == cxTemp)
{
CopyRect(&rcWork, &rcScreen);
cxMBMax = MultDiv(rcWork.right - rcWork.left, 7, 8);
goto ReSize;
}
}
if (hFontOld)
SelectFont(hdc, hFontOld);
NtUserReleaseDC(NULL, hdc);
// Find the window position
xMB = (rcWork.left + rcWork.right - cxBox) / 2 + (gpsi->cntMBox * SYSMET(CXSIZE));
xMB = max(xMB, 0);
yMB = (rcWork.top + rcWork.bottom - cyBox) / 2 + (gpsi->cntMBox * SYSMET(CYSIZE));
yMB = max(yMB, 0);
// Bottom, right justify if we're going off the screen--but leave a
// little gap
if (xMB + cxBox > rcWork.right)
xMB = rcWork.right - SYSMET(CXEDGE) - cxBox;
//
// Pin to the working area. If it won't fit, then pin to the screen
// height. Bottom justify it at least if too big even for that, so
// that the buttons are visible.
//
if (yMB + cyBox > rcWork.bottom)
{
yMB = rcWork.bottom - SYSMET(CYEDGE) - cyBox;
if (yMB < rcWork.top)
yMB = rcScreen.bottom - SYSMET(CYEDGE) - cyBox;
}
wTextLen = wcslen(lpmb->lpszText);
// Find out the memory required for the Dlg template and try to alloc it
hTemplate = LocalAlloc(LMEM_ZEROINIT, MB_FindDlgTemplateSize(lpmb));
if (!hTemplate)
goto SMB_Exit;
lpDlgTmp = (LPBYTE) hTemplate;
//
// Setup the dialog style for the message box
//
dwStyleDlg = WS_POPUPWINDOW | WS_CAPTION | DS_ABSALIGN | DS_NOIDLEMSG |
DS_SETFONT | DS_3DLOOK;
if ((dwStyleMsg & MB_MODEMASK) == MB_SYSTEMMODAL)
dwStyleDlg |= DS_SYSMODAL | DS_SETFOREGROUND;
else
dwStyleDlg |= DS_MODALFRAME | WS_SYSMENU;
if (dwStyleMsg & MB_SETFOREGROUND)
dwStyleDlg |= DS_SETFOREGROUND;
// Add the Header of the Dlg Template
// BOGUS !!! don't ADD bools
lpDlgTmp = MB_UpdateDlgHdr((LPDLGTEMPLATE) lpDlgTmp, dwStyleDlg,
(BYTE) (lpmb->cButtons + (wIconOrdNum != 0) + (lpmb->lpszText != NULL)),
xMB, yMB, cxBox, cyBox, (LPWSTR)lpmb->lpszCaption, wCaptionLen);
//
// Center the buttons
//
cxFoo = (cxBox - 2*SYSMET(CXFIXEDFRAME) - cxButtons) / 2;
lpDlgTmp = MB_AddPushButtons((LPDLGITEMTEMPLATE)lpDlgTmp, lpmb, cxFoo,
cyBox - SYSMET(CYCAPTION) - (2 * SYSMET(CYFIXEDFRAME)) -
YPixFromYDU(DU_OUTERMARGIN, gpsi->cyMsgFontChar));
// Add Icon, if any, to the Dlg template
//
// The icon is always top justified. If the text is shorter than the
// height of the icon, we center it. Otherwise the text will start at
// the top.
//
if (wIconOrdNum) {
OrdNum[0] = 0xFFFF; // To indicate that an Ordinal number follows
OrdNum[1] = (WORD) wIconOrdNum;
lpDlgTmp = MB_UpdateDlgItem((LPDLGITEMTEMPLATE)lpDlgTmp, IDUSERICON, /* Control Id */
SS_ICON | WS_GROUP | WS_CHILD | WS_VISIBLE,
XPixFromXDU(DU_OUTERMARGIN, gpsi->cxMsgFontChar), // X co-ordinate
YPixFromYDU(DU_OUTERMARGIN, gpsi->cyMsgFontChar), // Y co-ordinate
0, 0, // For Icons, CX and CY are ignored, can be zero
OrdNum, // Ordinal number of Icon
sizeof(OrdNum)/sizeof(WCHAR), // Length of OrdNum
STATICCODE);
}
/* Add the Text of the Message to the Dlg Template */
if (lpmb->lpszText) {
//
// Center the text if shorter than the icon.
//
if (cyText >= cyIcon)
cxFoo = 0;
else
cxFoo = (cyIcon - cyText) / 2;
dwStyleText = SS_NOPREFIX | WS_GROUP | WS_CHILD | WS_VISIBLE | SS_EDITCONTROL;
if (dwStyleMsg & MB_RIGHT) {
dwStyleText |= SS_RIGHT;
xText = cxBox - (SYSMET(CXSIZE) + cxText);
} else {
dwStyleText |= SS_LEFT;
xText = cxIcon + XPixFromXDU(DU_OUTERMARGIN, gpsi->cxMsgFontChar);
}
MB_UpdateDlgItem((LPDLGITEMTEMPLATE)lpDlgTmp, -1, dwStyleText, xText,
YPixFromYDU(DU_OUTERMARGIN, gpsi->cyMsgFontChar) + cxFoo,
cxText, cyText,
(LPWSTR)lpmb->lpszText, wTextLen, STATICCODE);
}
// The dialog template is ready
NtUserCallNoParam(SFI_INCRMBOX);
try {
//
// Set the normal cursor
//
hcurOld = NtUserSetCursor(LoadCursor(NULL, IDC_ARROW));
lpmb->lpszIcon = (LPWSTR) hIcon; // BUGBUG - How to diff this from a resource?
if (!(lpmb->dwStyle & MB_USERICON))
{
UNICODE_STRING strSound;
int wBeep = (LOWORD(lpmb->dwStyle & MB_ICONMASK)) >> MB_MASKSHIFT;
if (!(wBeep >= (sizeof(iconsnd) / sizeof(WCHAR *)))) {
RtlInitUnicodeString(&strSound, iconsnd[wBeep]);
NtUserPlayEventSound(&strSound);
}
}
iRetVal = (int)InternalDialogBox(hmodUser, hTemplate, 0L, hwndOwner,
MB_DlgProcW, (LPARAM) lpmb, FALSE);
//
// Fix up return value
if (iRetVal == -1)
iRetVal = 0; /* Messagebox should also return error */
//
// If the messagebox contains only OK button, then its ID is changed as
// IDCANCEL in MB_DlgProc; So, we must change it back to IDOK irrespective
// of whether ESC is pressed or Carriage return is pressed;
//
if (((dwStyleMsg & MB_TYPEMASK) == MB_OK) && iRetVal)
iRetVal = IDOK;
} finally {
NtUserCallNoParam(SFI_DECRMBOX);
}
//
// Restore the previous cursor
//
if (hcurOld)
NtUserSetCursor(hcurOld);
SMB_Exit:
if (hTemplate)
UserLocalFree(hTemplate);
if (hCaption) {
UserLocalFree(hCaption);
}
if (hText) {
UserLocalFree(hText);
}
return(iRetVal);
}