1 /* 2 * ReactOS kernel 3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 */ 19 /* 20 * PROJECT: ReactOS user32.dll 21 * FILE: win32ss/user/user32/windows/messagebox.c 22 * PURPOSE: Message Boxes 23 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) 24 * Thomas Weidenmueller (w3seek@users.sourceforge.net) 25 * UPDATE HISTORY: 26 * 2003/07/28 Added some NT features 27 * 2003/07/27 Code ported from wine 28 * 09-05-2001 CSH Created 29 */ 30 31 #include <user32.h> 32 33 WINE_DEFAULT_DEBUG_CHANNEL(user32); 34 35 /* DEFINES *******************************************************************/ 36 37 #define MSGBOX_IDICON (1088) 38 #define MSGBOX_IDTEXT (0xffff) 39 40 #define IDI_HANDW MAKEINTRESOURCEW(32513) 41 #define IDI_QUESTIONW MAKEINTRESOURCEW(32514) 42 #define IDI_EXCLAMATIONW MAKEINTRESOURCEW(32515) 43 #define IDI_ASTERISKW MAKEINTRESOURCEW(32516) 44 #define IDI_WINLOGOW MAKEINTRESOURCEW(32517) 45 46 47 /* MessageBox metrics */ 48 49 #define BTN_CX (75) 50 #define BTN_CY (23) 51 52 #define MSGBOXEX_SPACING (16) 53 #define MSGBOXEX_BUTTONSPACING (6) 54 #define MSGBOXEX_MARGIN (12) 55 #define MSGBOXEX_MAXBTNSTR (32) 56 #define MSGBOXEX_MAXBTNS (4) 57 58 /* Rescale logical coordinates */ 59 #define RESCALE_X(_x, _units) (((_x) * 4 + (_units).cx - 1) / (_units).cx) 60 #define RESCALE_Y(_y, _units) (((_y) * 8 + (_units).cy - 1) / (_units).cy) 61 62 63 /* MessageBox button helpers */ 64 65 #define DECLARE_MB_1(_btn0) \ 66 { 1, { ID##_btn0, 0, 0 }, { IDS_##_btn0, 0, 0 } } 67 68 #define DECLARE_MB_2(_btn0, _btn1) \ 69 { 2, { ID##_btn0, ID##_btn1, 0 }, { IDS_##_btn0, IDS_##_btn1, 0 } } 70 71 #define DECLARE_MB_3(_btn0, _btn1, _btn2) \ 72 { 3, { ID##_btn0, ID##_btn1, ID##_btn2 }, { IDS_##_btn0, IDS_##_btn1, IDS_##_btn2 } } 73 74 typedef struct _MSGBTNINFO 75 { 76 DWORD btnCnt; 77 INT btnIdx[MSGBOXEX_MAXBTNS]; 78 UINT btnIds[MSGBOXEX_MAXBTNS]; 79 } MSGBTNINFO, *PMSGBTNINFO; 80 81 /* Default MessageBox buttons */ 82 static const MSGBTNINFO MsgBtnInfo[] = 83 { 84 /* MB_OK (0) */ 85 DECLARE_MB_1(OK), 86 /* MB_OKCANCEL (1) */ 87 DECLARE_MB_2(OK, CANCEL), 88 /* MB_ABORTRETRYIGNORE (2) */ 89 DECLARE_MB_3(ABORT, RETRY, IGNORE), 90 /* MB_YESNOCANCEL (3) */ 91 DECLARE_MB_3(YES, NO, CANCEL), 92 /* MB_YESNO (4) */ 93 DECLARE_MB_2(YES, NO), 94 /* MB_RETRYCANCEL (5) */ 95 DECLARE_MB_2(RETRY, CANCEL), 96 /* MB_CANCELTRYCONTINUE (6) */ 97 DECLARE_MB_3(CANCEL, TRYAGAIN, CONTINUE) 98 }; 99 100 101 /* INTERNAL FUNCTIONS ********************************************************/ 102 103 static VOID MessageBoxTextToClipboard(HWND DialogWindow) 104 { 105 HWND hwndText; 106 PMSGBOXDATA mbd; 107 int cchTotal, cchTitle, cchText, cchButton, i, n, cchBuffer; 108 LPWSTR pszBuffer, pszBufferPos, pMessageBoxText, pszTitle, pszText, pszButton; 109 WCHAR szButton[MSGBOXEX_MAXBTNSTR]; 110 HGLOBAL hGlobal; 111 112 static const WCHAR szLine[] = L"---------------------------\r\n"; 113 114 mbd = (PMSGBOXDATA)GetPropW(DialogWindow, L"ROS_MSGBOX"); 115 hwndText = GetDlgItem(DialogWindow, MSGBOX_IDTEXT); 116 cchTitle = GetWindowTextLengthW(DialogWindow) + 1; 117 cchText = GetWindowTextLengthW(hwndText) + 1; 118 119 if (!mbd) 120 return; 121 122 pMessageBoxText = (LPWSTR)RtlAllocateHeap(GetProcessHeap(), 0, (cchTitle + cchText) * sizeof(WCHAR)); 123 124 if (pMessageBoxText == NULL) 125 { 126 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText); 127 return; 128 } 129 130 pszTitle = pMessageBoxText; 131 pszText = pMessageBoxText + cchTitle; 132 133 if (GetWindowTextW(DialogWindow, pszTitle, cchTitle) == 0 || 134 GetWindowTextW(hwndText, pszText, cchText) == 0) 135 { 136 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText); 137 return; 138 } 139 140 /* 141 * Calculate the total buffer size. 142 */ 143 cchTotal = 6 + cchTitle + cchText + (lstrlenW(szLine) * 4) + (mbd->dwButtons * MSGBOXEX_MAXBTNSTR + 3); 144 145 hGlobal = GlobalAlloc(GHND, cchTotal * sizeof(WCHAR)); 146 147 pszBuffer = (LPWSTR)GlobalLock(hGlobal); 148 149 if (pszBuffer == NULL) 150 { 151 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText); 152 GlobalFree(hGlobal); 153 return; 154 } 155 156 /* 157 * First format title and text. 158 * ------------------ 159 * Title 160 * ------------------ 161 * Text 162 * ------------------ 163 */ 164 cchBuffer = wsprintfW(pszBuffer, L"%s%s\r\n%s%s\r\n%s", szLine, pszTitle, szLine, pszText, szLine); 165 pszBufferPos = pszBuffer + cchBuffer; 166 167 for (i = 0; i < mbd->dwButtons; i++) 168 { 169 GetDlgItemTextW(DialogWindow, mbd->pidButton[i], szButton, MSGBOXEX_MAXBTNSTR); 170 171 cchButton = strlenW(szButton); 172 pszButton = szButton; 173 174 /* Skip '&' character. */ 175 if (szButton[0] == '&') 176 { 177 pszButton = pszButton + 1; 178 cchButton = cchButton - 1; 179 } 180 181 for (n = 0; n < cchButton; n++) 182 *(pszBufferPos++) = pszButton[n]; 183 184 /* Add spaces. */ 185 *(pszBufferPos++) = L' '; 186 *(pszBufferPos++) = L' '; 187 *(pszBufferPos++) = L' '; 188 } 189 190 wsprintfW(pszBufferPos, L"\r\n%s", szLine); 191 192 GlobalUnlock(hGlobal); 193 194 if (OpenClipboard(DialogWindow)) 195 { 196 EmptyClipboard(); 197 SetClipboardData(CF_UNICODETEXT, hGlobal); 198 CloseClipboard(); 199 } 200 else 201 { 202 GlobalFree(hGlobal); 203 } 204 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText); 205 } 206 207 static INT_PTR CALLBACK MessageBoxProc( 208 HWND hwnd, UINT message, 209 WPARAM wParam, LPARAM lParam) 210 { 211 int Alert; 212 PMSGBOXDATA mbd; 213 HELPINFO hi; 214 HWND hwndOwner; 215 216 switch (message) 217 { 218 case WM_INITDIALOG: 219 { 220 mbd = (PMSGBOXDATA)lParam; 221 222 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)mbd); 223 NtUserxSetMessageBox(hwnd); 224 225 if (!GetPropW(hwnd, L"ROS_MSGBOX")) 226 { 227 SetPropW(hwnd, L"ROS_MSGBOX", (HANDLE)lParam); 228 229 if (mbd->mbp.dwContextHelpId) 230 SetWindowContextHelpId(hwnd, mbd->mbp.dwContextHelpId); 231 232 if (mbd->mbp.lpszIcon) 233 { 234 SendDlgItemMessageW(hwnd, MSGBOX_IDICON, STM_SETICON, (WPARAM)(HICON)mbd->mbp.lpszIcon, 0); 235 Alert = ALERT_SYSTEM_WARNING; 236 } 237 else // Setup the rest of the alerts. 238 { 239 switch (mbd->mbp.dwStyle & MB_ICONMASK) 240 { 241 case MB_ICONWARNING: 242 Alert = ALERT_SYSTEM_WARNING; 243 break; 244 case MB_ICONERROR: 245 Alert = ALERT_SYSTEM_ERROR; 246 break; 247 case MB_ICONQUESTION: 248 Alert = ALERT_SYSTEM_QUERY; 249 break; 250 default: 251 Alert = ALERT_SYSTEM_INFORMATIONAL; 252 /* fall through */ 253 } 254 } 255 /* Send out the alert notifications. */ 256 NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd, OBJID_ALERT, Alert); 257 258 switch (mbd->mbp.dwStyle & MB_TYPEMASK) 259 { 260 case MB_ABORTRETRYIGNORE: 261 case MB_YESNO: 262 RemoveMenu(GetSystemMenu(hwnd, FALSE), SC_CLOSE, MF_BYCOMMAND); 263 break; 264 } 265 ASSERT(mbd->uDefButton < mbd->dwButtons); 266 SetFocus(GetDlgItem(hwnd, mbd->pidButton[mbd->uDefButton])); 267 if (mbd->dwTimeout && (mbd->dwTimeout != (UINT)-1)) 268 SetTimer(hwnd, 0, mbd->dwTimeout, NULL); 269 } 270 return 0; 271 } 272 273 case WM_COMMAND: 274 switch (LOWORD(wParam)) 275 { 276 case IDOK: 277 case IDCANCEL: 278 case IDABORT: 279 case IDRETRY: 280 case IDIGNORE: 281 case IDYES: 282 case IDNO: 283 case IDTRYAGAIN: 284 case IDCONTINUE: 285 EndDialog(hwnd, wParam); 286 return 0; 287 case IDHELP: 288 /* send WM_HELP message to messagebox window */ 289 hi.cbSize = sizeof(hi); 290 hi.iContextType = HELPINFO_WINDOW; 291 hi.iCtrlId = LOWORD(wParam); 292 hi.hItemHandle = (HANDLE)lParam; 293 hi.dwContextId = 0; 294 GetCursorPos(&hi.MousePos); 295 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi); 296 return 0; 297 } 298 return 0; 299 300 case WM_COPY: 301 MessageBoxTextToClipboard(hwnd); 302 return 0; 303 304 case WM_HELP: 305 { 306 mbd = (PMSGBOXDATA)GetPropW(hwnd, L"ROS_MSGBOX"); 307 if (!mbd) 308 return 0; 309 memcpy(&hi, (void *)lParam, sizeof(hi)); 310 hi.dwContextId = GetWindowContextHelpId(hwnd); 311 312 if (mbd->mbp.lpfnMsgBoxCallback) 313 { 314 mbd->mbp.lpfnMsgBoxCallback(&hi); 315 } 316 else 317 { 318 hwndOwner = GetWindow(hwnd, GW_OWNER); 319 if (hwndOwner) 320 SendMessageW(hwndOwner, WM_HELP, 0, (LPARAM)&hi); 321 } 322 return 0; 323 } 324 325 case WM_CLOSE: 326 { 327 mbd = (PMSGBOXDATA)GetPropW(hwnd, L"ROS_MSGBOX"); 328 if (!mbd) 329 return 0; 330 switch (mbd->mbp.dwStyle & MB_TYPEMASK) 331 { 332 case MB_ABORTRETRYIGNORE: 333 case MB_YESNO: 334 return 1; 335 } 336 EndDialog(hwnd, IDCANCEL); 337 return 1; 338 } 339 340 case WM_TIMER: 341 if (wParam == 0) 342 { 343 EndDialog(hwnd, IDTIMEOUT); 344 } 345 return 0; 346 } 347 return 0; 348 } 349 350 static int 351 MessageBoxTimeoutIndirectW( 352 CONST MSGBOXPARAMSW *lpMsgBoxParams, UINT dwTimeout) 353 { 354 int ret = 0; 355 MSGBOXDATA mbd; 356 MSGBTNINFO Buttons; 357 LPCWSTR ButtonText[MSGBOXEX_MAXBTNS]; 358 DLGTEMPLATE *tpl; 359 DLGITEMTEMPLATE *iico, *itxt, *ibtn; 360 NONCLIENTMETRICSW nclm; 361 LPVOID buf; 362 BYTE *dest; 363 LPCWSTR caption, text; 364 HFONT hFont, hOldFont; 365 HICON hIcon; 366 HWND hDCWnd; 367 HDC hDC; 368 SIZE units; 369 int bufsize, caplen, textlen, i, btnleft, btntop; 370 size_t ButtonLen; 371 RECT btnrect, txtrect, rc; 372 SIZE btnsize; 373 POINT iconPos; SIZE iconSize; 374 375 376 ZeroMemory(&mbd, sizeof(mbd)); 377 memcpy(&mbd.mbp, lpMsgBoxParams, sizeof(mbd.mbp)); 378 lpMsgBoxParams = &mbd.mbp; 379 380 mbd.wLanguageId = (WORD)lpMsgBoxParams->dwLanguageId; // FIXME! 381 mbd.dwTimeout = dwTimeout; 382 383 /* Create the selected buttons; unknown types will fall back to MB_OK */ 384 i = (lpMsgBoxParams->dwStyle & MB_TYPEMASK); 385 if (i >= ARRAYSIZE(MsgBtnInfo)) 386 i = MB_OK; 387 388 /* Get buttons IDs */ 389 Buttons = MsgBtnInfo[i]; 390 391 /* Add the Help button */ 392 if (lpMsgBoxParams->dwStyle & MB_HELP) 393 { 394 Buttons.btnIdx[Buttons.btnCnt] = IDHELP; 395 Buttons.btnIds[Buttons.btnCnt] = IDS_HELP; 396 Buttons.btnCnt++; 397 } 398 399 ASSERT(Buttons.btnCnt <= MSGBOXEX_MAXBTNS); 400 401 mbd.pidButton = Buttons.btnIdx; 402 mbd.ppszButtonText = ButtonText; 403 mbd.dwButtons = Buttons.btnCnt; 404 405 mbd.uDefButton = ((lpMsgBoxParams->dwStyle & MB_DEFMASK) >> 8); 406 /* Make the first button the default button if none other is */ 407 if (mbd.uDefButton >= mbd.dwButtons) 408 mbd.uDefButton = 0; 409 // mbd.uCancelId; 410 411 412 /* Load the caption */ 413 if (!lpMsgBoxParams->lpszCaption) 414 { 415 /* No caption, use the default one */ 416 caplen = LoadStringW(User32Instance, IDS_ERROR, (LPWSTR)&caption, 0); 417 } 418 else if (IS_INTRESOURCE(lpMsgBoxParams->lpszCaption)) 419 { 420 /* User-defined resource string */ 421 caplen = LoadStringW(lpMsgBoxParams->hInstance, 422 PtrToUlong(lpMsgBoxParams->lpszCaption), 423 (LPWSTR)&caption, 0); 424 } 425 else 426 { 427 /* UNICODE string pointer */ 428 caption = lpMsgBoxParams->lpszCaption; 429 caplen = wcslen(caption); 430 } 431 432 /* Load the text */ 433 if (!lpMsgBoxParams->lpszText) 434 { 435 /* No text, use blank */ 436 text = L""; 437 textlen = 0; 438 } 439 else if (IS_INTRESOURCE(lpMsgBoxParams->lpszText)) 440 { 441 /* User-defined resource string */ 442 textlen = LoadStringW(lpMsgBoxParams->hInstance, 443 PtrToUlong(lpMsgBoxParams->lpszText), 444 (LPWSTR)&text, 0); 445 } 446 else 447 { 448 /* UNICODE string pointer */ 449 text = lpMsgBoxParams->lpszText; 450 textlen = wcslen(text); 451 } 452 453 /* Load the icon */ 454 switch (lpMsgBoxParams->dwStyle & MB_ICONMASK) 455 { 456 case MB_ICONEXCLAMATION: // case MB_ICONWARNING: 457 hIcon = LoadIconW(NULL, IDI_EXCLAMATIONW); 458 MessageBeep(MB_ICONEXCLAMATION); 459 break; 460 case MB_ICONQUESTION: 461 hIcon = LoadIconW(NULL, IDI_QUESTIONW); 462 MessageBeep(MB_ICONQUESTION); 463 break; 464 case MB_ICONASTERISK: // case MB_ICONINFORMATION: 465 hIcon = LoadIconW(NULL, IDI_ASTERISKW); 466 MessageBeep(MB_ICONASTERISK); 467 break; 468 case MB_ICONHAND: // case MB_ICONSTOP: case MB_ICONERROR: 469 hIcon = LoadIconW(NULL, IDI_HANDW); 470 MessageBeep(MB_ICONHAND); 471 break; 472 case MB_USERICON: 473 hIcon = LoadIconW(lpMsgBoxParams->hInstance, lpMsgBoxParams->lpszIcon); 474 MessageBeep(MB_OK); 475 break; 476 default: 477 /* 478 * By default, Windows 95/98/NT does not associate an icon 479 * to message boxes. So ReactOS should do the same. 480 */ 481 hIcon = NULL; 482 MessageBeep(MB_OK); 483 break; 484 } 485 /* Reuse the internal pointer! */ 486 ((MSGBOXPARAMSW*)lpMsgBoxParams)->lpszIcon = (LPCWSTR)hIcon; 487 488 /* Basic space */ 489 bufsize = sizeof(DLGTEMPLATE) + 490 2 * sizeof(WORD) + /* menu and class */ 491 (caplen + 1) * sizeof(WCHAR) + /* title */ 492 sizeof(WORD); /* font height */ 493 494 /* Space for the icon */ 495 if (hIcon) 496 { 497 bufsize = ALIGN_UP(bufsize, DWORD); 498 bufsize += sizeof(DLGITEMTEMPLATE) + 499 4 * sizeof(WORD) + 500 sizeof(WCHAR); 501 } 502 503 /* Space for the text */ 504 bufsize = ALIGN_UP(bufsize, DWORD); 505 bufsize += sizeof(DLGITEMTEMPLATE) + 506 3 * sizeof(WORD) + 507 (textlen + 1) * sizeof(WCHAR); 508 509 /* Space for the buttons */ 510 for (i = 0; i < mbd.dwButtons; i++) 511 { 512 /* Get the default text of the buttons */ 513 if (Buttons.btnIds[i]) 514 { 515 LPCWSTR pStr; 516 ButtonLen = LoadStringW(User32Instance, 517 Buttons.btnIds[i], 518 (LPWSTR)&pStr, 0); 519 mbd.ppszButtonText[i] = RtlAllocateHeap(GetProcessHeap(), 0, (ButtonLen + 1) * sizeof(WCHAR)); 520 if (mbd.ppszButtonText[i]) 521 { 522 memcpy((LPWSTR)mbd.ppszButtonText[i], pStr, ButtonLen * sizeof(WCHAR)); 523 ((LPWSTR)mbd.ppszButtonText[i])[ButtonLen] = 0; 524 } 525 else 526 { 527 mbd.ppszButtonText[i] = L""; 528 ButtonLen = 0; 529 } 530 } 531 else 532 { 533 /* No text, use blank */ 534 mbd.ppszButtonText[i] = L""; 535 ButtonLen = 0; 536 } 537 538 bufsize = ALIGN_UP(bufsize, DWORD); 539 bufsize += sizeof(DLGITEMTEMPLATE) + 540 3 * sizeof(WORD) + 541 (ButtonLen + 1) * sizeof(WCHAR); 542 } 543 544 /* Allocate the dialog template */ 545 buf = RtlAllocateHeap(GetProcessHeap(), 0, bufsize); 546 if (!buf) 547 goto Quit; 548 549 iico = itxt = NULL; 550 551 552 nclm.cbSize = sizeof(nclm); 553 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(nclm), &nclm, 0); 554 hFont = CreateFontIndirectW(&nclm.lfMessageFont); 555 if (!hFont) 556 { 557 ERR("Cannot retrieve nclm.lfMessageFont!\n"); 558 goto Quit; 559 } 560 561 hDCWnd = NULL; 562 hDC = GetDCEx(hDCWnd, NULL, DCX_WINDOW | DCX_CACHE); 563 if (!hDC) 564 { 565 /* Retry with the DC of the owner window */ 566 hDCWnd = lpMsgBoxParams->hwndOwner; 567 hDC = GetDCEx(hDCWnd, NULL, DCX_WINDOW | DCX_CACHE); 568 } 569 if (!hDC) 570 { 571 ERR("GetDCEx() failed, bail out!\n"); 572 goto Quit; 573 } 574 hOldFont = SelectObject(hDC, hFont); 575 576 units.cx = GdiGetCharDimensions(hDC, NULL, &units.cy); 577 if (!units.cx) 578 { 579 DWORD defUnits; 580 ERR("GdiGetCharDimensions() failed, falling back to default values!\n"); 581 defUnits = GetDialogBaseUnits(); 582 units.cx = LOWORD(defUnits); 583 units.cy = HIWORD(defUnits); 584 } 585 586 /* Calculate the caption rectangle */ 587 txtrect.right = MulDiv(GetSystemMetrics(SM_CXSCREEN), 4, 5); 588 if (hIcon) 589 txtrect.right -= GetSystemMetrics(SM_CXICON) + MSGBOXEX_SPACING; 590 txtrect.top = txtrect.left = txtrect.bottom = 0; 591 if (textlen != 0) 592 { 593 DrawTextW(hDC, text, textlen, &txtrect, 594 DT_LEFT | DT_NOPREFIX | DT_WORDBREAK | DT_EXPANDTABS | DT_EXTERNALLEADING | DT_EDITCONTROL | DT_CALCRECT); 595 } 596 else 597 { 598 txtrect.right = txtrect.left + 1; 599 txtrect.bottom = txtrect.top + 1; 600 } 601 txtrect.right++; 602 603 /* Calculate the maximum buttons size */ 604 btnsize.cx = BTN_CX; 605 btnsize.cy = BTN_CY; 606 btnrect.left = btnrect.top = 0; 607 for (i = 0; i < mbd.dwButtons; i++) 608 { 609 // btnrect.right = btnrect.bottom = 0; // FIXME: Is it needed?? 610 DrawTextW(hDC, mbd.ppszButtonText[i], wcslen(mbd.ppszButtonText[i]), 611 &btnrect, DT_LEFT | DT_SINGLELINE | DT_CALCRECT); 612 btnsize.cx = max(btnsize.cx, btnrect.right); 613 btnsize.cy = max(btnsize.cy, btnrect.bottom); 614 } 615 616 if (hOldFont) 617 SelectObject(hDC, hOldFont); 618 619 ReleaseDC(hDCWnd, hDC); 620 621 if (hFont) 622 DeleteObject(hFont); 623 624 625 /* Calculate position and size of controls */ 626 627 628 /* Calculate position and size of the icon */ 629 rc.left = rc.bottom = rc.right = 0; 630 btntop = 0; 631 if (hIcon) 632 { 633 rc.right = GetSystemMetrics(SM_CXICON); 634 rc.bottom = GetSystemMetrics(SM_CYICON); 635 #ifdef MSGBOX_ICONVCENTER 636 rc.top = MSGBOXEX_MARGIN + ((max(txtrect.bottom, rc.bottom) - rc.bottom) / 2); 637 rc.top = max(MSGBOXEX_SPACING, rc.top); 638 #else 639 rc.top = MSGBOXEX_MARGIN; 640 #endif 641 btnleft = (mbd.dwButtons * (btnsize.cx + MSGBOXEX_BUTTONSPACING)) - MSGBOXEX_BUTTONSPACING; 642 if (btnleft > txtrect.right + rc.right + MSGBOXEX_SPACING) 643 { 644 #ifdef MSGBOX_TEXTHCENTER 645 rc.left = MSGBOXEX_MARGIN + ((btnleft - txtrect.right - rc.right - MSGBOXEX_SPACING) / 2); 646 #else 647 rc.left = MSGBOXEX_MARGIN; 648 #endif 649 btnleft = MSGBOXEX_MARGIN; 650 } 651 else 652 { 653 rc.left = MSGBOXEX_MARGIN; 654 btnleft = MSGBOXEX_MARGIN + ((txtrect.right + rc.right + MSGBOXEX_SPACING - btnleft) / 2); 655 } 656 657 iconPos.x = RESCALE_X(rc.left, units); 658 iconPos.y = RESCALE_Y(rc.top, units); 659 iconSize.cx = RESCALE_X(rc.right, units); 660 iconSize.cy = RESCALE_Y(rc.bottom, units); 661 662 btntop = rc.top + rc.bottom + MSGBOXEX_SPACING; 663 rc.left += rc.right + MSGBOXEX_SPACING; 664 } 665 else 666 { 667 btnleft = (mbd.dwButtons * (btnsize.cx + MSGBOXEX_BUTTONSPACING)) - MSGBOXEX_BUTTONSPACING; 668 if (btnleft > txtrect.right) 669 { 670 #ifdef MSGBOX_TEXTHCENTER 671 rc.left = MSGBOXEX_MARGIN + ((btnleft - txtrect.right) / 2); 672 #else 673 rc.left = MSGBOXEX_MARGIN; 674 #endif 675 btnleft = MSGBOXEX_MARGIN; 676 } 677 else 678 { 679 rc.left = MSGBOXEX_MARGIN; 680 btnleft = MSGBOXEX_MARGIN + ((txtrect.right - btnleft) / 2); 681 } 682 } 683 684 685 686 /* Initialize the dialog template */ 687 tpl = (DLGTEMPLATE *)buf; 688 689 tpl->style = WS_CAPTION | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_SYSMENU | 690 DS_CENTER | DS_SETFONT | DS_MODALFRAME | DS_NOIDLEMSG; 691 tpl->dwExtendedStyle = WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT; 692 if (lpMsgBoxParams->dwStyle & MB_TOPMOST) 693 tpl->dwExtendedStyle |= WS_EX_TOPMOST; 694 if (lpMsgBoxParams->dwStyle & MB_RIGHT) 695 tpl->dwExtendedStyle |= WS_EX_RIGHT; 696 tpl->x = 100; 697 tpl->y = 100; 698 tpl->cdit = mbd.dwButtons + (hIcon ? 1 : 0) + 1; /* Buttons, icon and text */ 699 700 dest = (BYTE *)(tpl + 1); 701 702 *(DWORD*)dest = 0; /* no menu and use default window class */ 703 dest += 2 * sizeof(WORD); 704 memcpy(dest, caption, caplen * sizeof(WCHAR)); 705 dest += caplen * sizeof(WCHAR); 706 *(WCHAR*)dest = L'\0'; 707 dest += sizeof(WCHAR); 708 709 /* 710 * A font point size (height) of 0x7FFF means that we use 711 * the message box font (NONCLIENTMETRICSW.lfMessageFont). 712 */ 713 *(WORD*)dest = 0x7FFF; 714 dest += sizeof(WORD); 715 716 /* Create the icon */ 717 if (hIcon) 718 { 719 dest = ALIGN_UP_POINTER(dest, DWORD); 720 iico = (DLGITEMTEMPLATE *)dest; 721 iico->style = WS_CHILD | WS_VISIBLE | SS_ICON; 722 iico->dwExtendedStyle = 0; 723 iico->id = MSGBOX_IDICON; 724 725 iico->x = iconPos.x; 726 iico->y = iconPos.y; 727 iico->cx = iconSize.cx; 728 iico->cy = iconSize.cy; 729 730 dest += sizeof(DLGITEMTEMPLATE); 731 *(WORD*)dest = 0xFFFF; 732 dest += sizeof(WORD); 733 *(WORD*)dest = 0x0082; /* static control */ 734 dest += sizeof(WORD); 735 *(WORD*)dest = 0xFFFF; 736 dest += sizeof(WORD); 737 *(WCHAR*)dest = 0; 738 dest += sizeof(WCHAR); 739 *(WORD*)dest = 0; 740 dest += sizeof(WORD); 741 } 742 743 /* Create static for text */ 744 dest = ALIGN_UP_POINTER(dest, DWORD); 745 itxt = (DLGITEMTEMPLATE *)dest; 746 itxt->style = WS_CHILD | WS_VISIBLE | SS_NOPREFIX; 747 if (lpMsgBoxParams->dwStyle & MB_RIGHT) 748 itxt->style |= SS_RIGHT; 749 else 750 itxt->style |= SS_LEFT; 751 itxt->dwExtendedStyle = 0; 752 itxt->id = MSGBOX_IDTEXT; 753 dest += sizeof(DLGITEMTEMPLATE); 754 *(WORD*)dest = 0xFFFF; 755 dest += sizeof(WORD); 756 *(WORD*)dest = 0x0082; /* static control */ 757 dest += sizeof(WORD); 758 memcpy(dest, text, textlen * sizeof(WCHAR)); 759 dest += textlen * sizeof(WCHAR); 760 *(WCHAR*)dest = 0; 761 dest += sizeof(WCHAR); 762 *(WORD*)dest = 0; 763 dest += sizeof(WORD); 764 765 766 /* Calculate position of the text */ 767 rc.top = MSGBOXEX_MARGIN + ((rc.bottom - txtrect.bottom) / 2); 768 rc.top = max(rc.top, MSGBOXEX_MARGIN); 769 770 771 /* Make the first button the default button if none other is */ 772 if (mbd.uDefButton >= mbd.dwButtons) 773 mbd.uDefButton = 0; 774 775 /* Create and calculate the position of the buttons */ 776 btntop = max(rc.top + txtrect.bottom + MSGBOXEX_SPACING, btntop); 777 for (i = 0; i < mbd.dwButtons; i++) 778 { 779 ButtonLen = wcslen(mbd.ppszButtonText[i]); 780 781 dest = ALIGN_UP_POINTER(dest, DWORD); 782 ibtn = (DLGITEMTEMPLATE *)dest; 783 784 ibtn->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP; 785 if (i == mbd.uDefButton) 786 ibtn->style |= BS_DEFPUSHBUTTON; 787 else 788 ibtn->style |= BS_PUSHBUTTON; 789 790 ibtn->dwExtendedStyle = 0; 791 ibtn->id = mbd.pidButton[i]; 792 dest += sizeof(DLGITEMTEMPLATE); 793 *(WORD*)dest = 0xFFFF; 794 dest += sizeof(WORD); 795 *(WORD*)dest = 0x0080; /* button control */ 796 dest += sizeof(WORD); 797 memcpy(dest, mbd.ppszButtonText[i], ButtonLen * sizeof(WCHAR)); 798 dest += ButtonLen * sizeof(WCHAR); 799 *(WORD*)dest = 0; 800 dest += sizeof(WORD); 801 *(WORD*)dest = 0; 802 dest += sizeof(WORD); 803 804 ibtn->x = RESCALE_X(btnleft, units); 805 ibtn->y = RESCALE_Y(btntop, units); 806 ibtn->cx = RESCALE_X(btnsize.cx, units); 807 ibtn->cy = RESCALE_Y(btnsize.cy, units); 808 btnleft += btnsize.cx + MSGBOXEX_BUTTONSPACING; 809 } 810 811 /* Calculate the size and position of the messagebox window */ 812 btnleft = max(btnleft - MSGBOXEX_BUTTONSPACING, rc.left + txtrect.right); 813 btnleft += MSGBOXEX_MARGIN; 814 btntop += btnsize.cy + MSGBOXEX_MARGIN; 815 816 /* Set the size and position of the static message */ 817 itxt->x = RESCALE_X(rc.left, units); 818 itxt->y = RESCALE_Y(rc.top, units); 819 itxt->cx = RESCALE_X(btnleft - rc.left - MSGBOXEX_MARGIN, units); 820 itxt->cy = RESCALE_Y(txtrect.bottom, units); 821 822 /* Set the size of the window */ 823 tpl->cx = RESCALE_X(btnleft, units); 824 tpl->cy = RESCALE_Y(btntop, units); 825 826 /* Finally show the messagebox */ 827 ret = DialogBoxIndirectParamW(lpMsgBoxParams->hInstance, tpl, 828 lpMsgBoxParams->hwndOwner, 829 MessageBoxProc, (LPARAM)&mbd); 830 831 Quit: 832 RtlFreeHeap(GetProcessHeap(), 0, buf); 833 834 for (i = 0; i < mbd.dwButtons; i++) 835 { 836 if (mbd.ppszButtonText[i] && *(mbd.ppszButtonText[i])) 837 RtlFreeHeap(GetProcessHeap(), 0, (LPWSTR)mbd.ppszButtonText[i]); 838 } 839 840 return ret; 841 } 842 843 844 /* FUNCTIONS *****************************************************************/ 845 846 /* 847 * @implemented 848 */ 849 int 850 WINAPI 851 MessageBoxA( 852 IN HWND hWnd, 853 IN LPCSTR lpText, 854 IN LPCSTR lpCaption, 855 IN UINT uType) 856 { 857 return MessageBoxExA(hWnd, lpText, lpCaption, uType, LANG_NEUTRAL); 858 } 859 860 /* 861 * @implemented 862 */ 863 int 864 WINAPI 865 MessageBoxW( 866 IN HWND hWnd, 867 IN LPCWSTR lpText, 868 IN LPCWSTR lpCaption, 869 IN UINT uType) 870 { 871 return MessageBoxExW(hWnd, lpText, lpCaption, uType, LANG_NEUTRAL); 872 } 873 874 875 /* 876 * @implemented 877 */ 878 int 879 WINAPI 880 MessageBoxExA( 881 IN HWND hWnd, 882 IN LPCSTR lpText, 883 IN LPCSTR lpCaption, 884 IN UINT uType, 885 IN WORD wLanguageId) 886 { 887 MSGBOXPARAMSA msgbox; 888 889 msgbox.cbSize = sizeof(msgbox); 890 msgbox.hwndOwner = hWnd; 891 msgbox.hInstance = 0; 892 msgbox.lpszText = lpText; 893 msgbox.lpszCaption = lpCaption; 894 msgbox.dwStyle = uType; 895 msgbox.lpszIcon = NULL; 896 msgbox.dwContextHelpId = 0; 897 msgbox.lpfnMsgBoxCallback = NULL; 898 msgbox.dwLanguageId = wLanguageId; 899 900 return MessageBoxIndirectA(&msgbox); 901 } 902 903 /* 904 * @implemented 905 */ 906 int 907 WINAPI 908 MessageBoxExW( 909 IN HWND hWnd, 910 IN LPCWSTR lpText, 911 IN LPCWSTR lpCaption, 912 IN UINT uType, 913 IN WORD wLanguageId) 914 { 915 MSGBOXPARAMSW msgbox; 916 917 msgbox.cbSize = sizeof(msgbox); 918 msgbox.hwndOwner = hWnd; 919 msgbox.hInstance = 0; 920 msgbox.lpszText = lpText; 921 msgbox.lpszCaption = lpCaption; 922 msgbox.dwStyle = uType; 923 msgbox.lpszIcon = NULL; 924 msgbox.dwContextHelpId = 0; 925 msgbox.lpfnMsgBoxCallback = NULL; 926 msgbox.dwLanguageId = wLanguageId; 927 928 return MessageBoxTimeoutIndirectW(&msgbox, (UINT)-1); 929 } 930 931 932 /* 933 * @implemented 934 */ 935 int 936 WINAPI 937 MessageBoxIndirectA( 938 IN CONST MSGBOXPARAMSA* lpMsgBoxParams) 939 { 940 MSGBOXPARAMSW msgboxW; 941 UNICODE_STRING textW, captionW, iconW; 942 int ret; 943 944 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszText)) 945 { 946 RtlCreateUnicodeStringFromAsciiz(&textW, (PCSZ)lpMsgBoxParams->lpszText); 947 /* 948 * UNICODE_STRING objects are always allocated with an extra byte so you 949 * can null-term if you want 950 */ 951 textW.Buffer[textW.Length / sizeof(WCHAR)] = L'\0'; 952 } 953 else 954 textW.Buffer = (LPWSTR)lpMsgBoxParams->lpszText; 955 956 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszCaption)) 957 { 958 RtlCreateUnicodeStringFromAsciiz(&captionW, (PCSZ)lpMsgBoxParams->lpszCaption); 959 /* 960 * UNICODE_STRING objects are always allocated with an extra byte so you 961 * can null-term if you want 962 */ 963 captionW.Buffer[captionW.Length / sizeof(WCHAR)] = L'\0'; 964 } 965 else 966 captionW.Buffer = (LPWSTR)lpMsgBoxParams->lpszCaption; 967 968 if (lpMsgBoxParams->dwStyle & MB_USERICON) 969 { 970 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszIcon)) 971 { 972 RtlCreateUnicodeStringFromAsciiz(&iconW, (PCSZ)lpMsgBoxParams->lpszIcon); 973 /* 974 * UNICODE_STRING objects are always allocated with an extra byte so you 975 * can null-term if you want 976 */ 977 iconW.Buffer[iconW.Length / sizeof(WCHAR)] = L'\0'; 978 } 979 else 980 iconW.Buffer = (LPWSTR)lpMsgBoxParams->lpszIcon; 981 } 982 else 983 iconW.Buffer = NULL; 984 985 msgboxW.cbSize = sizeof(msgboxW); 986 msgboxW.hwndOwner = lpMsgBoxParams->hwndOwner; 987 msgboxW.hInstance = lpMsgBoxParams->hInstance; 988 msgboxW.lpszText = textW.Buffer; 989 msgboxW.lpszCaption = captionW.Buffer; 990 msgboxW.dwStyle = lpMsgBoxParams->dwStyle; 991 msgboxW.lpszIcon = iconW.Buffer; 992 msgboxW.dwContextHelpId = lpMsgBoxParams->dwContextHelpId; 993 msgboxW.lpfnMsgBoxCallback = lpMsgBoxParams->lpfnMsgBoxCallback; 994 msgboxW.dwLanguageId = lpMsgBoxParams->dwLanguageId; 995 996 ret = MessageBoxTimeoutIndirectW(&msgboxW, (UINT)-1); 997 998 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszText)) 999 RtlFreeUnicodeString(&textW); 1000 1001 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszCaption)) 1002 RtlFreeUnicodeString(&captionW); 1003 1004 if ((lpMsgBoxParams->dwStyle & MB_USERICON) && !IS_INTRESOURCE(iconW.Buffer)) 1005 RtlFreeUnicodeString(&iconW); 1006 1007 return ret; 1008 } 1009 1010 /* 1011 * @implemented 1012 */ 1013 int 1014 WINAPI 1015 MessageBoxIndirectW( 1016 IN CONST MSGBOXPARAMSW* lpMsgBoxParams) 1017 { 1018 return MessageBoxTimeoutIndirectW(lpMsgBoxParams, (UINT)-1); 1019 } 1020 1021 1022 /* 1023 * @implemented 1024 */ 1025 int 1026 WINAPI 1027 MessageBoxTimeoutA( 1028 IN HWND hWnd, 1029 IN LPCSTR lpText, 1030 IN LPCSTR lpCaption, 1031 IN UINT uType, 1032 IN WORD wLanguageId, 1033 IN DWORD dwTimeout) 1034 { 1035 MSGBOXPARAMSW msgboxW; 1036 UNICODE_STRING textW, captionW; 1037 int ret; 1038 1039 if (!IS_INTRESOURCE(lpText)) 1040 RtlCreateUnicodeStringFromAsciiz(&textW, (PCSZ)lpText); 1041 else 1042 textW.Buffer = (LPWSTR)lpText; 1043 1044 if (!IS_INTRESOURCE(lpCaption)) 1045 RtlCreateUnicodeStringFromAsciiz(&captionW, (PCSZ)lpCaption); 1046 else 1047 captionW.Buffer = (LPWSTR)lpCaption; 1048 1049 msgboxW.cbSize = sizeof(msgboxW); 1050 msgboxW.hwndOwner = hWnd; 1051 msgboxW.hInstance = 0; 1052 msgboxW.lpszText = textW.Buffer; 1053 msgboxW.lpszCaption = captionW.Buffer; 1054 msgboxW.dwStyle = uType; 1055 msgboxW.lpszIcon = NULL; 1056 msgboxW.dwContextHelpId = 0; 1057 msgboxW.lpfnMsgBoxCallback = NULL; 1058 msgboxW.dwLanguageId = wLanguageId; 1059 1060 ret = MessageBoxTimeoutIndirectW(&msgboxW, (UINT)dwTimeout); 1061 1062 if (!IS_INTRESOURCE(textW.Buffer)) 1063 RtlFreeUnicodeString(&textW); 1064 1065 if (!IS_INTRESOURCE(captionW.Buffer)) 1066 RtlFreeUnicodeString(&captionW); 1067 1068 return ret; 1069 } 1070 1071 /* 1072 * @implemented 1073 */ 1074 int 1075 WINAPI 1076 MessageBoxTimeoutW( 1077 IN HWND hWnd, 1078 IN LPCWSTR lpText, 1079 IN LPCWSTR lpCaption, 1080 IN UINT uType, 1081 IN WORD wLanguageId, 1082 IN DWORD dwTimeout) 1083 { 1084 MSGBOXPARAMSW msgbox; 1085 1086 msgbox.cbSize = sizeof(msgbox); 1087 msgbox.hwndOwner = hWnd; 1088 msgbox.hInstance = 0; 1089 msgbox.lpszText = lpText; 1090 msgbox.lpszCaption = lpCaption; 1091 msgbox.dwStyle = uType; 1092 msgbox.lpszIcon = NULL; 1093 msgbox.dwContextHelpId = 0; 1094 msgbox.lpfnMsgBoxCallback = NULL; 1095 msgbox.dwLanguageId = wLanguageId; 1096 1097 return MessageBoxTimeoutIndirectW(&msgbox, (UINT)dwTimeout); 1098 } 1099 1100 1101 /* 1102 * @unimplemented 1103 */ 1104 DWORD 1105 WINAPI 1106 SoftModalMessageBox(DWORD Unknown0) 1107 { 1108 UNIMPLEMENTED; 1109 return 0; 1110 } 1111 1112 1113 /* 1114 * @implemented 1115 */ 1116 BOOL 1117 WINAPI 1118 MessageBeep(IN UINT uType) 1119 { 1120 return NtUserxMessageBeep(uType); 1121 } 1122 1123 1124 /* 1125 * @implemented 1126 * 1127 * See: https://msdn.microsoft.com/en-us/library/windows/desktop/dn910915(v=vs.85).aspx 1128 * and: http://undoc.airesoft.co.uk/user32.dll/MB_GetString.php 1129 * for more information. 1130 */ 1131 LPCWSTR 1132 WINAPI 1133 MB_GetString(IN UINT wBtn) 1134 { 1135 LPCWSTR btnStr = NULL; 1136 1137 /* 1138 * The allowable IDs are between "IDOK - 1" (0) and "IDCONTINUE - 1" (10) inclusive. 1139 * See psdk/winuser.h and user32/include/resource.h . 1140 */ 1141 if (wBtn > IDCONTINUE - 1) 1142 return NULL; 1143 1144 wBtn += 800; // See user32/include/resource.h 1145 LoadStringW(User32Instance, wBtn, (LPWSTR)&btnStr, 0); 1146 return btnStr; 1147 } 1148 1149 /* EOF */ 1150