3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
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.
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.
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.
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 * Hermes Belusca-Maito
27 * 2003/07/28 Added some NT features
28 * 2003/07/27 Code ported from wine
29 * 09-05-2001 CSH Created
33 #include <ndk/exfuncs.h>
35 #include <ntstrsafe.h>
37 WINE_DEFAULT_DEBUG_CHANNEL(user32
);
39 /* DEFINES *******************************************************************/
41 #define MSGBOX_IDICON (1088)
42 #define MSGBOX_IDTEXT (0xffff)
44 #define IDI_HANDW MAKEINTRESOURCEW(32513)
45 #define IDI_QUESTIONW MAKEINTRESOURCEW(32514)
46 #define IDI_EXCLAMATIONW MAKEINTRESOURCEW(32515)
47 #define IDI_ASTERISKW MAKEINTRESOURCEW(32516)
48 #define IDI_WINLOGOW MAKEINTRESOURCEW(32517)
51 /* MessageBox metrics */
56 #define MSGBOXEX_SPACING (16)
57 #define MSGBOXEX_BUTTONSPACING (6)
58 #define MSGBOXEX_MARGIN (12)
59 #define MSGBOXEX_MAXBTNSTR (32)
60 #define MSGBOXEX_MAXBTNS (4)
62 /* Rescale logical coordinates */
63 #define RESCALE_X(_x, _units) (((_x) * 4 + (_units).cx - 1) / (_units).cx)
64 #define RESCALE_Y(_y, _units) (((_y) * 8 + (_units).cy - 1) / (_units).cy)
67 /* MessageBox button helpers */
69 #define DECLARE_MB_1(_btn0) \
70 { 1, { ID##_btn0, 0, 0 }, { IDS_##_btn0, 0, 0 } }
72 #define DECLARE_MB_2(_btn0, _btn1) \
73 { 2, { ID##_btn0, ID##_btn1, 0 }, { IDS_##_btn0, IDS_##_btn1, 0 } }
75 #define DECLARE_MB_3(_btn0, _btn1, _btn2) \
76 { 3, { ID##_btn0, ID##_btn1, ID##_btn2 }, { IDS_##_btn0, IDS_##_btn1, IDS_##_btn2 } }
78 typedef struct _MSGBTNINFO
81 INT btnIdx
[MSGBOXEX_MAXBTNS
];
82 UINT btnIds
[MSGBOXEX_MAXBTNS
];
83 } MSGBTNINFO
, *PMSGBTNINFO
;
85 /* Default MessageBox buttons */
86 static const MSGBTNINFO MsgBtnInfo
[] =
91 DECLARE_MB_2(OK
, CANCEL
),
92 /* MB_ABORTRETRYIGNORE (2) */
93 DECLARE_MB_3(ABORT
, RETRY
, IGNORE
),
94 /* MB_YESNOCANCEL (3) */
95 DECLARE_MB_3(YES
, NO
, CANCEL
),
97 DECLARE_MB_2(YES
, NO
),
98 /* MB_RETRYCANCEL (5) */
99 DECLARE_MB_2(RETRY
, CANCEL
),
100 /* MB_CANCELTRYCONTINUE (6) */
101 DECLARE_MB_3(CANCEL
, TRYAGAIN
, CONTINUE
)
105 /* INTERNAL FUNCTIONS ********************************************************/
109 IN HINSTANCE hInstance OPTIONAL
,
112 IN PCWSTR pDefaultString OPTIONAL
)
117 /* Try to load the string from the resource */
118 Length
= LoadStringW(hInstance
, uID
, (LPWSTR
)&pStr
, 0);
121 /* If the resource string was not found, use the fallback default one */
125 /* None was specified, return NULL */
130 pStr
= pDefaultString
;
131 Length
= wcslen(pStr
);
134 /* Allocate a new buffer, adding a NULL-terminator */
135 *pString
= RtlAllocateHeap(RtlGetProcessHeap(), 0, (Length
+ 1) * sizeof(WCHAR
));
139 /* Copy the string, NULL-terminated */
140 RtlStringCchCopyNW(*pString
, Length
+ 1, pStr
, Length
);
144 static VOID
MessageBoxTextToClipboard(HWND DialogWindow
)
148 int cchTotal
, cchTitle
, cchText
, cchButton
, i
, n
, cchBuffer
;
149 LPWSTR pszBuffer
, pszBufferPos
, pMessageBoxText
, pszTitle
, pszText
, pszButton
;
150 WCHAR szButton
[MSGBOXEX_MAXBTNSTR
];
153 static const WCHAR szLine
[] = L
"---------------------------\r\n";
155 mbd
= (PMSGBOXDATA
)GetPropW(DialogWindow
, L
"ROS_MSGBOX");
156 hwndText
= GetDlgItem(DialogWindow
, MSGBOX_IDTEXT
);
157 cchTitle
= GetWindowTextLengthW(DialogWindow
) + 1;
158 cchText
= GetWindowTextLengthW(hwndText
) + 1;
163 pMessageBoxText
= (LPWSTR
)RtlAllocateHeap(GetProcessHeap(), 0, (cchTitle
+ cchText
) * sizeof(WCHAR
));
165 if (pMessageBoxText
== NULL
)
167 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText
);
171 pszTitle
= pMessageBoxText
;
172 pszText
= pMessageBoxText
+ cchTitle
;
174 if (GetWindowTextW(DialogWindow
, pszTitle
, cchTitle
) == 0 ||
175 GetWindowTextW(hwndText
, pszText
, cchText
) == 0)
177 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText
);
182 * Calculate the total buffer size.
184 cchTotal
= 6 + cchTitle
+ cchText
+ (lstrlenW(szLine
) * 4) + (mbd
->dwButtons
* MSGBOXEX_MAXBTNSTR
+ 3);
186 hGlobal
= GlobalAlloc(GHND
, cchTotal
* sizeof(WCHAR
));
188 pszBuffer
= (LPWSTR
)GlobalLock(hGlobal
);
190 if (pszBuffer
== NULL
)
192 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText
);
198 * First format title and text.
205 cchBuffer
= wsprintfW(pszBuffer
, L
"%s%s\r\n%s%s\r\n%s", szLine
, pszTitle
, szLine
, pszText
, szLine
);
206 pszBufferPos
= pszBuffer
+ cchBuffer
;
208 for (i
= 0; i
< mbd
->dwButtons
; i
++)
210 GetDlgItemTextW(DialogWindow
, mbd
->pidButton
[i
], szButton
, MSGBOXEX_MAXBTNSTR
);
212 cchButton
= strlenW(szButton
);
213 pszButton
= szButton
;
215 /* Skip '&' character. */
216 if (szButton
[0] == '&')
218 pszButton
= pszButton
+ 1;
219 cchButton
= cchButton
- 1;
222 for (n
= 0; n
< cchButton
; n
++)
223 *(pszBufferPos
++) = pszButton
[n
];
226 *(pszBufferPos
++) = L
' ';
227 *(pszBufferPos
++) = L
' ';
228 *(pszBufferPos
++) = L
' ';
231 wsprintfW(pszBufferPos
, L
"\r\n%s", szLine
);
233 GlobalUnlock(hGlobal
);
235 if (OpenClipboard(DialogWindow
))
238 SetClipboardData(CF_UNICODETEXT
, hGlobal
);
245 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText
);
248 static INT_PTR CALLBACK
MessageBoxProc(
249 HWND hwnd
, UINT message
,
250 WPARAM wParam
, LPARAM lParam
)
260 mbd
= (PMSGBOXDATA
)lParam
;
262 SetWindowLongPtrW(hwnd
, GWLP_USERDATA
, (LONG_PTR
)mbd
);
263 NtUserxSetMessageBox(hwnd
);
265 if (!GetPropW(hwnd
, L
"ROS_MSGBOX"))
267 SetPropW(hwnd
, L
"ROS_MSGBOX", (HANDLE
)lParam
);
269 if (mbd
->mbp
.dwContextHelpId
)
270 SetWindowContextHelpId(hwnd
, mbd
->mbp
.dwContextHelpId
);
272 if (mbd
->mbp
.lpszIcon
)
274 SendDlgItemMessageW(hwnd
, MSGBOX_IDICON
, STM_SETICON
, (WPARAM
)(HICON
)mbd
->mbp
.lpszIcon
, 0);
275 Alert
= ALERT_SYSTEM_WARNING
;
277 else /* Setup the rest of the alerts */
279 switch (mbd
->mbp
.dwStyle
& MB_ICONMASK
)
282 Alert
= ALERT_SYSTEM_WARNING
;
285 Alert
= ALERT_SYSTEM_ERROR
;
287 case MB_ICONQUESTION
:
288 Alert
= ALERT_SYSTEM_QUERY
;
291 Alert
= ALERT_SYSTEM_INFORMATIONAL
;
295 /* Send out the alert notifications */
296 NotifyWinEvent(EVENT_SYSTEM_ALERT
, hwnd
, OBJID_ALERT
, Alert
);
298 /* Disable the Close menu button if no Cancel button is specified */
299 if (mbd
->uCancelId
== 0)
301 HMENU hSysMenu
= GetSystemMenu(hwnd
, FALSE
);
303 DeleteMenu(hSysMenu
, SC_CLOSE
, MF_BYCOMMAND
);
306 /* Set the focus to the default button */
307 if (mbd
->dwButtons
> 0)
309 ASSERT(mbd
->uDefButton
< mbd
->dwButtons
);
310 SetFocus(GetDlgItem(hwnd
, mbd
->pidButton
[mbd
->uDefButton
]));
313 /* Set up the window timer */
314 if (mbd
->dwTimeout
&& (mbd
->dwTimeout
!= (UINT
)-1))
315 SetTimer(hwnd
, 0, mbd
->dwTimeout
, NULL
);
323 INT_PTR iCtrlId
= LOWORD(wParam
);
327 /* Handle the default message-box buttons */
331 * The dialog manager always sends IDCANCEL when the user
332 * presses ESCape. We check here whether the message box
333 * has a CANCEL button, or whether we should fall back to
334 * the OK button, by using the correct uCancelId.
336 if (iCtrlId
== IDCANCEL
)
338 mbd
= (PMSGBOXDATA
)GetPropW(hwnd
, L
"ROS_MSGBOX");
340 return FALSE
; /* Ignore */
342 /* Check whether we can cancel the message box */
343 if (mbd
->uCancelId
== 0)
344 return TRUE
; // FALSE; /* No, ignore */
345 /* Quit with the correct return value */
346 iCtrlId
= mbd
->uCancelId
;
348 if (!GetDlgItem(hwnd
, iCtrlId
))
349 return FALSE
; /* Ignore */
359 EndDialog(hwnd
, iCtrlId
);
363 return FALSE
; /* Ignore */
367 /* Send WM_HELP message to the message-box window */
369 hi
.cbSize
= sizeof(hi
);
370 hi
.iContextType
= HELPINFO_WINDOW
;
371 hi
.iCtrlId
= iCtrlId
;
372 hi
.hItemHandle
= (HANDLE
)lParam
;
374 GetCursorPos(&hi
.MousePos
);
375 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
383 /* Check for any other user-defined buttons */
384 mbd
= (PMSGBOXDATA
)GetPropW(hwnd
, L
"ROS_MSGBOX");
388 for (i
= 0; i
< mbd
->dwButtons
; ++i
)
390 if (iCtrlId
== mbd
->pidButton
[i
])
392 EndDialog(hwnd
, iCtrlId
);
401 MessageBoxTextToClipboard(hwnd
);
406 LPHELPINFO phi
= (LPHELPINFO
)lParam
;
407 mbd
= (PMSGBOXDATA
)GetPropW(hwnd
, L
"ROS_MSGBOX");
410 phi
->dwContextId
= GetWindowContextHelpId(hwnd
);
412 if (mbd
->mbp
.lpfnMsgBoxCallback
)
414 mbd
->mbp
.lpfnMsgBoxCallback(phi
);
418 HWND hwndOwner
= GetWindow(hwnd
, GW_OWNER
);
420 SendMessageW(hwndOwner
, WM_HELP
, 0, lParam
);
427 mbd
= (PMSGBOXDATA
)GetPropW(hwnd
, L
"ROS_MSGBOX");
431 /* Check whether we can cancel the message box */
432 if (mbd
->uCancelId
== 0)
433 return TRUE
; /* No, ignore */
434 /* Quit with the correct return value */
435 EndDialog(hwnd
, mbd
->uCancelId
);
441 EndDialog(hwnd
, IDTIMEOUT
);
449 MessageBoxTimeoutIndirectW(
450 CONST MSGBOXPARAMSW
*lpMsgBoxParams
, UINT dwTimeout
)
454 LPWSTR defCaption
= NULL
;
457 LPCWSTR ButtonText
[MSGBOXEX_MAXBTNS
];
459 // TODO: Check whether the caller is an NT 3.x app and if so, check
460 // instead for the MB_SERVICE_NOTIFICATION_NT3X flag and adjust it.
461 if (lpMsgBoxParams
->dwStyle
& MB_SERVICE_NOTIFICATION
)
464 UNICODE_STRING CaptionU
, TextU
;
465 ULONG Response
= ResponseNotHandled
; /* HARDERROR_RESPONSE */
466 ULONG_PTR MsgBoxParams
[4] =
469 (ULONG_PTR
)&CaptionU
,
471 * Retrieve the message box flags. Note that we filter out
472 * MB_SERVICE_NOTIFICATION to not enter an infinite recursive
473 * loop when we will call MessageBox() later on.
475 lpMsgBoxParams
->dwStyle
& ~MB_SERVICE_NOTIFICATION
,
479 /* hwndOwner must be NULL */
480 if (lpMsgBoxParams
->hwndOwner
!= NULL
)
482 ERR("MessageBoxTimeoutIndirectW(MB_SERVICE_NOTIFICATION): hwndOwner is not NULL!\n");
487 // FIXME: TODO: Implement the special case for Terminal Services.
490 RtlInitUnicodeString(&CaptionU
, lpMsgBoxParams
->lpszCaption
);
491 RtlInitUnicodeString(&TextU
, lpMsgBoxParams
->lpszText
);
493 Status
= NtRaiseHardError(STATUS_SERVICE_NOTIFICATION
| HARDERROR_OVERRIDE_ERRORMODE
,
494 ARRAYSIZE(MsgBoxParams
),
497 OptionOk
, /* NOTE: This parameter is ignored */
499 if (!NT_SUCCESS(Status
))
501 ERR("MessageBoxTimeoutIndirectW(MB_SERVICE_NOTIFICATION): NtRaiseHardError failed, Status = 0x%08lx\n", Status
);
505 /* Map the returned response to the buttons */
509 case ResponseReturnToCaller
:
510 case ResponseNotHandled
:
527 case ResponseTryAgain
:
529 case ResponseContinue
:
539 ZeroMemory(&mbd
, sizeof(mbd
));
540 memcpy(&mbd
.mbp
, lpMsgBoxParams
, sizeof(mbd
.mbp
));
541 mbd
.wLanguageId
= (WORD
)lpMsgBoxParams
->dwLanguageId
;
542 mbd
.dwTimeout
= dwTimeout
;
544 if (!mbd
.mbp
.lpszCaption
)
546 /* No caption, use the default one */
547 LoadAllocStringW(User32Instance
,
551 mbd
.mbp
.lpszCaption
= (defCaption
? defCaption
: L
"Error");
554 /* Create the selected buttons; unknown types will fall back to MB_OK */
555 i
= (lpMsgBoxParams
->dwStyle
& MB_TYPEMASK
);
556 if (i
>= ARRAYSIZE(MsgBtnInfo
))
559 /* Get the buttons IDs */
560 Buttons
= MsgBtnInfo
[i
];
562 /* Add the Help button */
563 if (lpMsgBoxParams
->dwStyle
& MB_HELP
)
565 Buttons
.btnIdx
[Buttons
.btnCnt
] = IDHELP
;
566 Buttons
.btnIds
[Buttons
.btnCnt
] = IDS_HELP
;
570 ASSERT(Buttons
.btnCnt
<= MSGBOXEX_MAXBTNS
);
572 /* Retrieve the pointers to the button labels and find the Cancel button */
573 mbd
.uCancelId
= (i
== MB_OK
? IDOK
: 0);
574 for (i
= 0; i
< Buttons
.btnCnt
; ++i
)
576 // FIXME: Use the strings in the correct language.
577 // MB_GetString gives the string in default system language.
578 ButtonText
[i
] = MB_GetString(Buttons
.btnIds
[i
] - IDS_OK
); /* or: Buttons.btnIdx[i] - IDOK */
580 LoadAllocStringW(User32Instance
,
585 if (Buttons
.btnIdx
[i
] == IDCANCEL
)
586 mbd
.uCancelId
= IDCANCEL
;
589 mbd
.pidButton
= Buttons
.btnIdx
;
590 mbd
.ppszButtonText
= ButtonText
;
591 mbd
.dwButtons
= Buttons
.btnCnt
;
593 mbd
.uDefButton
= ((lpMsgBoxParams
->dwStyle
& MB_DEFMASK
) >> 8);
594 /* Make the first button the default button if none other is */
595 if (mbd
.uDefButton
>= mbd
.dwButtons
)
598 /* Call the helper function */
599 ret
= SoftModalMessageBox(&mbd
);
602 for (i
= 0; i
< mbd
.dwButtons
; i
++)
604 if (ButtonText
[i
] && *ButtonText
[i
])
605 RtlFreeHeap(RtlGetProcessHeap(), 0, ButtonText
[i
]);
610 RtlFreeHeap(RtlGetProcessHeap(), 0, defCaption
);
617 SoftModalMessageBox(IN LPMSGBOXDATA lpMsgBoxData
)
621 LPMSGBOXPARAMSW lpMsgBoxParams
= &mbd
.mbp
;
623 DLGITEMTEMPLATE
*iico
, *itxt
, *ibtn
;
624 NONCLIENTMETRICSW nclm
;
627 LPWSTR caption
, text
;
628 HFONT hFont
, hOldFont
;
633 int bufsize
, caplen
, textlen
, i
, btnleft
, btntop
;
635 RECT btnrect
, txtrect
, rc
;
637 POINT iconPos
; SIZE iconSize
;
639 /* Capture the MsgBoxData */
640 memcpy(&mbd
, lpMsgBoxData
, sizeof(mbd
));
642 /* Load the caption */
644 if (lpMsgBoxParams
->lpszCaption
&& IS_INTRESOURCE(lpMsgBoxParams
->lpszCaption
))
646 /* User-defined resource string */
647 caplen
= LoadAllocStringW(lpMsgBoxParams
->hInstance
,
648 PtrToUlong(lpMsgBoxParams
->lpszCaption
),
651 lpMsgBoxParams
->lpszCaption
= caption
;
653 else if (lpMsgBoxParams
->lpszCaption
)
655 /* UNICODE string pointer */
656 caplen
= wcslen(lpMsgBoxParams
->lpszCaption
);
658 if (!lpMsgBoxParams
->lpszCaption
)
660 /* No caption, use blank */
661 lpMsgBoxParams
->lpszCaption
= L
"";
667 if (lpMsgBoxParams
->lpszText
&& IS_INTRESOURCE(lpMsgBoxParams
->lpszText
))
669 /* User-defined resource string */
670 textlen
= LoadAllocStringW(lpMsgBoxParams
->hInstance
,
671 PtrToUlong(lpMsgBoxParams
->lpszText
),
674 lpMsgBoxParams
->lpszText
= text
;
676 else if (lpMsgBoxParams
->lpszText
)
678 /* UNICODE string pointer */
679 textlen
= wcslen(lpMsgBoxParams
->lpszText
);
681 if (!lpMsgBoxParams
->lpszText
)
683 /* No text, use blank */
684 lpMsgBoxParams
->lpszText
= L
"";
689 switch (lpMsgBoxParams
->dwStyle
& MB_ICONMASK
)
691 case MB_ICONEXCLAMATION
: // case MB_ICONWARNING:
692 hIcon
= LoadIconW(NULL
, IDI_EXCLAMATIONW
);
693 MessageBeep(MB_ICONEXCLAMATION
);
695 case MB_ICONQUESTION
:
696 hIcon
= LoadIconW(NULL
, IDI_QUESTIONW
);
697 MessageBeep(MB_ICONQUESTION
);
699 case MB_ICONASTERISK
: // case MB_ICONINFORMATION:
700 hIcon
= LoadIconW(NULL
, IDI_ASTERISKW
);
701 MessageBeep(MB_ICONASTERISK
);
703 case MB_ICONHAND
: // case MB_ICONSTOP: case MB_ICONERROR:
704 hIcon
= LoadIconW(NULL
, IDI_HANDW
);
705 MessageBeep(MB_ICONHAND
);
708 hIcon
= LoadIconW(lpMsgBoxParams
->hInstance
, lpMsgBoxParams
->lpszIcon
);
713 * By default, Windows 95/98/NT does not associate an icon
714 * to message boxes. So ReactOS should do the same.
720 /* Reuse the internal pointer! */
721 lpMsgBoxParams
->lpszIcon
= (LPCWSTR
)hIcon
;
724 bufsize
= sizeof(DLGTEMPLATE
) +
725 2 * sizeof(WORD
) + /* menu and class */
726 (caplen
+ 1) * sizeof(WCHAR
) + /* title */
727 sizeof(WORD
); /* font height */
729 /* Space for the icon */
732 bufsize
= ALIGN_UP(bufsize
, DWORD
);
733 bufsize
+= sizeof(DLGITEMTEMPLATE
) +
738 /* Space for the text */
739 bufsize
= ALIGN_UP(bufsize
, DWORD
);
740 bufsize
+= sizeof(DLGITEMTEMPLATE
) +
742 (textlen
+ 1) * sizeof(WCHAR
);
744 /* Space for the buttons */
745 for (i
= 0; i
< mbd
.dwButtons
; i
++)
747 if (!mbd
.ppszButtonText
[i
] || !*mbd
.ppszButtonText
[i
])
749 /* No text, use blank */
750 mbd
.ppszButtonText
[i
] = L
"";
755 /* UNICODE string pointer */
756 ButtonLen
= wcslen(mbd
.ppszButtonText
[i
]);
759 bufsize
= ALIGN_UP(bufsize
, DWORD
);
760 bufsize
+= sizeof(DLGITEMTEMPLATE
) +
762 (ButtonLen
+ 1) * sizeof(WCHAR
);
765 /* Allocate the dialog template */
766 buf
= RtlAllocateHeap(RtlGetProcessHeap(), 0, bufsize
);
773 nclm
.cbSize
= sizeof(nclm
);
774 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(nclm
), &nclm
, 0);
775 hFont
= CreateFontIndirectW(&nclm
.lfMessageFont
);
778 ERR("Cannot retrieve nclm.lfMessageFont! (error %lu)\n", GetLastError());
783 hDC
= GetDCEx(hDCWnd
, NULL
, DCX_WINDOW
| DCX_CACHE
);
786 /* Retry with the DC of the owner window */
787 hDCWnd
= lpMsgBoxParams
->hwndOwner
;
788 hDC
= GetDCEx(hDCWnd
, NULL
, DCX_WINDOW
| DCX_CACHE
);
792 ERR("GetDCEx() failed, bail out! (error %lu)\n", GetLastError());
795 hOldFont
= SelectObject(hDC
, hFont
);
797 units
.cx
= GdiGetCharDimensions(hDC
, NULL
, &units
.cy
);
801 ERR("GdiGetCharDimensions() failed, falling back to default values (error %lu)\n", GetLastError());
802 defUnits
= GetDialogBaseUnits();
803 units
.cx
= LOWORD(defUnits
);
804 units
.cy
= HIWORD(defUnits
);
807 /* Calculate the caption rectangle */
808 txtrect
.right
= MulDiv(GetSystemMetrics(SM_CXSCREEN
), 4, 5);
810 txtrect
.right
-= GetSystemMetrics(SM_CXICON
) + MSGBOXEX_SPACING
;
811 txtrect
.top
= txtrect
.left
= txtrect
.bottom
= 0;
814 DrawTextW(hDC
, lpMsgBoxParams
->lpszText
, textlen
, &txtrect
,
815 DT_LEFT
| DT_NOPREFIX
| DT_WORDBREAK
| DT_EXPANDTABS
| DT_EXTERNALLEADING
| DT_EDITCONTROL
| DT_CALCRECT
);
819 txtrect
.right
= txtrect
.left
+ 1;
820 txtrect
.bottom
= txtrect
.top
+ 1;
824 /* Calculate the maximum buttons size */
827 btnrect
.left
= btnrect
.top
= 0;
828 for (i
= 0; i
< mbd
.dwButtons
; i
++)
830 // btnrect.right = btnrect.bottom = 0; // FIXME: Is it needed??
831 DrawTextW(hDC
, mbd
.ppszButtonText
[i
], wcslen(mbd
.ppszButtonText
[i
]),
832 &btnrect
, DT_LEFT
| DT_SINGLELINE
| DT_CALCRECT
);
833 btnsize
.cx
= max(btnsize
.cx
, btnrect
.right
);
834 btnsize
.cy
= max(btnsize
.cy
, btnrect
.bottom
);
838 SelectObject(hDC
, hOldFont
);
840 ReleaseDC(hDCWnd
, hDC
);
846 /* Calculate position and size of controls */
849 /* Calculate position and size of the icon */
850 rc
.left
= rc
.bottom
= rc
.right
= 0;
854 rc
.right
= GetSystemMetrics(SM_CXICON
);
855 rc
.bottom
= GetSystemMetrics(SM_CYICON
);
856 #ifdef MSGBOX_ICONVCENTER
857 rc
.top
= MSGBOXEX_MARGIN
+ ((max(txtrect
.bottom
, rc
.bottom
) - rc
.bottom
) / 2);
858 rc
.top
= max(MSGBOXEX_SPACING
, rc
.top
);
860 rc
.top
= MSGBOXEX_MARGIN
;
862 btnleft
= (mbd
.dwButtons
* (btnsize
.cx
+ MSGBOXEX_BUTTONSPACING
)) - MSGBOXEX_BUTTONSPACING
;
863 if (btnleft
> txtrect
.right
+ rc
.right
+ MSGBOXEX_SPACING
)
865 #ifdef MSGBOX_TEXTHCENTER
866 rc
.left
= MSGBOXEX_MARGIN
+ ((btnleft
- txtrect
.right
- rc
.right
- MSGBOXEX_SPACING
) / 2);
868 rc
.left
= MSGBOXEX_MARGIN
;
870 btnleft
= MSGBOXEX_MARGIN
;
874 rc
.left
= MSGBOXEX_MARGIN
;
875 btnleft
= MSGBOXEX_MARGIN
+ ((txtrect
.right
+ rc
.right
+ MSGBOXEX_SPACING
- btnleft
) / 2);
878 iconPos
.x
= RESCALE_X(rc
.left
, units
);
879 iconPos
.y
= RESCALE_Y(rc
.top
, units
);
880 iconSize
.cx
= RESCALE_X(rc
.right
, units
);
881 iconSize
.cy
= RESCALE_Y(rc
.bottom
, units
);
883 btntop
= rc
.top
+ rc
.bottom
+ MSGBOXEX_SPACING
;
884 rc
.left
+= rc
.right
+ MSGBOXEX_SPACING
;
888 btnleft
= (mbd
.dwButtons
* (btnsize
.cx
+ MSGBOXEX_BUTTONSPACING
)) - MSGBOXEX_BUTTONSPACING
;
889 if (btnleft
> txtrect
.right
)
891 #ifdef MSGBOX_TEXTHCENTER
892 rc
.left
= MSGBOXEX_MARGIN
+ ((btnleft
- txtrect
.right
) / 2);
894 rc
.left
= MSGBOXEX_MARGIN
;
896 btnleft
= MSGBOXEX_MARGIN
;
900 rc
.left
= MSGBOXEX_MARGIN
;
901 btnleft
= MSGBOXEX_MARGIN
+ ((txtrect
.right
- btnleft
) / 2);
906 /* Initialize the dialog template */
907 tpl
= (DLGTEMPLATE
*)buf
;
909 tpl
->style
= WS_CAPTION
| WS_POPUP
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_SYSMENU
|
910 DS_CENTER
| DS_SETFONT
| DS_MODALFRAME
| DS_NOIDLEMSG
;
911 tpl
->dwExtendedStyle
= WS_EX_DLGMODALFRAME
| WS_EX_WINDOWEDGE
| WS_EX_CONTROLPARENT
;
912 if (lpMsgBoxParams
->dwStyle
& MB_TOPMOST
)
913 tpl
->dwExtendedStyle
|= WS_EX_TOPMOST
;
914 if (lpMsgBoxParams
->dwStyle
& MB_RIGHT
)
915 tpl
->dwExtendedStyle
|= WS_EX_RIGHT
;
918 tpl
->cdit
= mbd
.dwButtons
+ (hIcon
? 1 : 0) + 1; /* Buttons, icon and text */
920 dest
= (BYTE
*)(tpl
+ 1);
922 *(DWORD
*)dest
= 0; /* no menu and use default window class */
923 dest
+= 2 * sizeof(WORD
);
924 memcpy(dest
, lpMsgBoxParams
->lpszCaption
, caplen
* sizeof(WCHAR
));
925 dest
+= caplen
* sizeof(WCHAR
);
926 *(WCHAR
*)dest
= L
'\0';
927 dest
+= sizeof(WCHAR
);
930 * A font point size (height) of 0x7FFF means that we use
931 * the message box font (NONCLIENTMETRICSW.lfMessageFont).
933 *(WORD
*)dest
= 0x7FFF;
934 dest
+= sizeof(WORD
);
936 /* Create the icon */
939 dest
= ALIGN_UP_POINTER(dest
, DWORD
);
940 iico
= (DLGITEMTEMPLATE
*)dest
;
941 iico
->style
= WS_CHILD
| WS_VISIBLE
| SS_ICON
;
942 iico
->dwExtendedStyle
= 0;
943 iico
->id
= MSGBOX_IDICON
;
947 iico
->cx
= iconSize
.cx
;
948 iico
->cy
= iconSize
.cy
;
950 dest
+= sizeof(DLGITEMTEMPLATE
);
951 *(WORD
*)dest
= 0xFFFF;
952 dest
+= sizeof(WORD
);
953 *(WORD
*)dest
= 0x0082; /* static control */
954 dest
+= sizeof(WORD
);
955 *(WORD
*)dest
= 0xFFFF;
956 dest
+= sizeof(WORD
);
958 dest
+= sizeof(WCHAR
);
960 dest
+= sizeof(WORD
);
963 /* Create static for text */
964 dest
= ALIGN_UP_POINTER(dest
, DWORD
);
965 itxt
= (DLGITEMTEMPLATE
*)dest
;
966 itxt
->style
= WS_CHILD
| WS_VISIBLE
| SS_NOPREFIX
;
967 if (lpMsgBoxParams
->dwStyle
& MB_RIGHT
)
968 itxt
->style
|= SS_RIGHT
;
970 itxt
->style
|= SS_LEFT
;
971 itxt
->dwExtendedStyle
= 0;
972 itxt
->id
= MSGBOX_IDTEXT
;
973 dest
+= sizeof(DLGITEMTEMPLATE
);
974 *(WORD
*)dest
= 0xFFFF;
975 dest
+= sizeof(WORD
);
976 *(WORD
*)dest
= 0x0082; /* static control */
977 dest
+= sizeof(WORD
);
978 memcpy(dest
, lpMsgBoxParams
->lpszText
, textlen
* sizeof(WCHAR
));
979 dest
+= textlen
* sizeof(WCHAR
);
981 dest
+= sizeof(WCHAR
);
983 dest
+= sizeof(WORD
);
986 /* Calculate position of the text */
987 rc
.top
= MSGBOXEX_MARGIN
+ ((rc
.bottom
- txtrect
.bottom
) / 2);
988 rc
.top
= max(rc
.top
, MSGBOXEX_MARGIN
);
991 /* Make the first button the default button if none other is */
992 if (mbd
.uDefButton
>= mbd
.dwButtons
)
995 /* Create and calculate the position of the buttons */
996 btntop
= max(rc
.top
+ txtrect
.bottom
+ MSGBOXEX_SPACING
, btntop
);
997 for (i
= 0; i
< mbd
.dwButtons
; i
++)
999 ButtonLen
= wcslen(mbd
.ppszButtonText
[i
]);
1001 dest
= ALIGN_UP_POINTER(dest
, DWORD
);
1002 ibtn
= (DLGITEMTEMPLATE
*)dest
;
1004 ibtn
->style
= WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
;
1005 if (i
== mbd
.uDefButton
)
1006 ibtn
->style
|= BS_DEFPUSHBUTTON
;
1008 ibtn
->style
|= BS_PUSHBUTTON
;
1010 ibtn
->dwExtendedStyle
= 0;
1011 ibtn
->id
= mbd
.pidButton
[i
];
1012 dest
+= sizeof(DLGITEMTEMPLATE
);
1013 *(WORD
*)dest
= 0xFFFF;
1014 dest
+= sizeof(WORD
);
1015 *(WORD
*)dest
= 0x0080; /* button control */
1016 dest
+= sizeof(WORD
);
1017 memcpy(dest
, mbd
.ppszButtonText
[i
], ButtonLen
* sizeof(WCHAR
));
1018 dest
+= ButtonLen
* sizeof(WCHAR
);
1020 dest
+= sizeof(WORD
);
1022 dest
+= sizeof(WORD
);
1024 ibtn
->x
= RESCALE_X(btnleft
, units
);
1025 ibtn
->y
= RESCALE_Y(btntop
, units
);
1026 ibtn
->cx
= RESCALE_X(btnsize
.cx
, units
);
1027 ibtn
->cy
= RESCALE_Y(btnsize
.cy
, units
);
1028 btnleft
+= btnsize
.cx
+ MSGBOXEX_BUTTONSPACING
;
1031 /* Calculate the size and position of the message-box window */
1032 btnleft
= max(btnleft
- MSGBOXEX_BUTTONSPACING
, rc
.left
+ txtrect
.right
);
1033 btnleft
+= MSGBOXEX_MARGIN
;
1034 if (mbd
.dwButtons
> 0)
1035 btntop
+= btnsize
.cy
+ MSGBOXEX_MARGIN
;
1037 /* Set the size and position of the static message */
1038 itxt
->x
= RESCALE_X(rc
.left
, units
);
1039 itxt
->y
= RESCALE_Y(rc
.top
, units
);
1040 itxt
->cx
= RESCALE_X(btnleft
- rc
.left
- MSGBOXEX_MARGIN
, units
);
1041 itxt
->cy
= RESCALE_Y(txtrect
.bottom
, units
);
1043 /* Set the size of the window */
1044 tpl
->cx
= RESCALE_X(btnleft
, units
);
1045 tpl
->cy
= RESCALE_Y(btntop
, units
);
1047 /* Finally show the message-box */
1048 ret
= DialogBoxIndirectParamW(lpMsgBoxParams
->hInstance
, tpl
,
1049 lpMsgBoxParams
->hwndOwner
,
1050 MessageBoxProc
, (LPARAM
)&mbd
);
1054 RtlFreeHeap(RtlGetProcessHeap(), 0, buf
);
1057 RtlFreeHeap(RtlGetProcessHeap(), 0, text
);
1060 RtlFreeHeap(RtlGetProcessHeap(), 0, caption
);
1066 /* FUNCTIONS *****************************************************************/
1076 IN LPCSTR lpCaption
,
1079 return MessageBoxExA(hWnd
, lpText
, lpCaption
, uType
, LANG_NEUTRAL
);
1090 IN LPCWSTR lpCaption
,
1093 return MessageBoxExW(hWnd
, lpText
, lpCaption
, uType
, LANG_NEUTRAL
);
1105 IN LPCSTR lpCaption
,
1107 IN WORD wLanguageId
)
1109 MSGBOXPARAMSA msgbox
;
1111 msgbox
.cbSize
= sizeof(msgbox
);
1112 msgbox
.hwndOwner
= hWnd
;
1113 msgbox
.hInstance
= 0;
1114 msgbox
.lpszText
= lpText
;
1115 msgbox
.lpszCaption
= lpCaption
;
1116 msgbox
.dwStyle
= uType
;
1117 msgbox
.lpszIcon
= NULL
;
1118 msgbox
.dwContextHelpId
= 0;
1119 msgbox
.lpfnMsgBoxCallback
= NULL
;
1120 msgbox
.dwLanguageId
= wLanguageId
;
1122 return MessageBoxIndirectA(&msgbox
);
1133 IN LPCWSTR lpCaption
,
1135 IN WORD wLanguageId
)
1137 MSGBOXPARAMSW msgbox
;
1139 msgbox
.cbSize
= sizeof(msgbox
);
1140 msgbox
.hwndOwner
= hWnd
;
1141 msgbox
.hInstance
= 0;
1142 msgbox
.lpszText
= lpText
;
1143 msgbox
.lpszCaption
= lpCaption
;
1144 msgbox
.dwStyle
= uType
;
1145 msgbox
.lpszIcon
= NULL
;
1146 msgbox
.dwContextHelpId
= 0;
1147 msgbox
.lpfnMsgBoxCallback
= NULL
;
1148 msgbox
.dwLanguageId
= wLanguageId
;
1150 return MessageBoxTimeoutIndirectW(&msgbox
, (UINT
)-1);
1159 MessageBoxIndirectA(
1160 IN CONST MSGBOXPARAMSA
* lpMsgBoxParams
)
1162 MSGBOXPARAMSW msgboxW
;
1163 UNICODE_STRING textW
, captionW
, iconW
;
1166 if (!IS_INTRESOURCE(lpMsgBoxParams
->lpszText
))
1168 RtlCreateUnicodeStringFromAsciiz(&textW
, (PCSZ
)lpMsgBoxParams
->lpszText
);
1170 * UNICODE_STRING objects are always allocated with an extra byte so you
1171 * can null-term if you want
1173 textW
.Buffer
[textW
.Length
/ sizeof(WCHAR
)] = L
'\0';
1176 textW
.Buffer
= (LPWSTR
)lpMsgBoxParams
->lpszText
;
1178 if (!IS_INTRESOURCE(lpMsgBoxParams
->lpszCaption
))
1180 RtlCreateUnicodeStringFromAsciiz(&captionW
, (PCSZ
)lpMsgBoxParams
->lpszCaption
);
1182 * UNICODE_STRING objects are always allocated with an extra byte so you
1183 * can null-term if you want
1185 captionW
.Buffer
[captionW
.Length
/ sizeof(WCHAR
)] = L
'\0';
1188 captionW
.Buffer
= (LPWSTR
)lpMsgBoxParams
->lpszCaption
;
1190 if (lpMsgBoxParams
->dwStyle
& MB_USERICON
)
1192 if (!IS_INTRESOURCE(lpMsgBoxParams
->lpszIcon
))
1194 RtlCreateUnicodeStringFromAsciiz(&iconW
, (PCSZ
)lpMsgBoxParams
->lpszIcon
);
1196 * UNICODE_STRING objects are always allocated with an extra byte so you
1197 * can null-term if you want
1199 iconW
.Buffer
[iconW
.Length
/ sizeof(WCHAR
)] = L
'\0';
1202 iconW
.Buffer
= (LPWSTR
)lpMsgBoxParams
->lpszIcon
;
1205 iconW
.Buffer
= NULL
;
1207 msgboxW
.cbSize
= sizeof(msgboxW
);
1208 msgboxW
.hwndOwner
= lpMsgBoxParams
->hwndOwner
;
1209 msgboxW
.hInstance
= lpMsgBoxParams
->hInstance
;
1210 msgboxW
.lpszText
= textW
.Buffer
;
1211 msgboxW
.lpszCaption
= captionW
.Buffer
;
1212 msgboxW
.dwStyle
= lpMsgBoxParams
->dwStyle
;
1213 msgboxW
.lpszIcon
= iconW
.Buffer
;
1214 msgboxW
.dwContextHelpId
= lpMsgBoxParams
->dwContextHelpId
;
1215 msgboxW
.lpfnMsgBoxCallback
= lpMsgBoxParams
->lpfnMsgBoxCallback
;
1216 msgboxW
.dwLanguageId
= lpMsgBoxParams
->dwLanguageId
;
1218 ret
= MessageBoxTimeoutIndirectW(&msgboxW
, (UINT
)-1);
1220 if (!IS_INTRESOURCE(lpMsgBoxParams
->lpszText
))
1221 RtlFreeUnicodeString(&textW
);
1223 if (!IS_INTRESOURCE(lpMsgBoxParams
->lpszCaption
))
1224 RtlFreeUnicodeString(&captionW
);
1226 if ((lpMsgBoxParams
->dwStyle
& MB_USERICON
) && !IS_INTRESOURCE(iconW
.Buffer
))
1227 RtlFreeUnicodeString(&iconW
);
1237 MessageBoxIndirectW(
1238 IN CONST MSGBOXPARAMSW
* lpMsgBoxParams
)
1240 return MessageBoxTimeoutIndirectW(lpMsgBoxParams
, (UINT
)-1);
1252 IN LPCSTR lpCaption
,
1254 IN WORD wLanguageId
,
1257 MSGBOXPARAMSW msgboxW
;
1258 UNICODE_STRING textW
, captionW
;
1261 if (!IS_INTRESOURCE(lpText
))
1262 RtlCreateUnicodeStringFromAsciiz(&textW
, (PCSZ
)lpText
);
1264 textW
.Buffer
= (LPWSTR
)lpText
;
1266 if (!IS_INTRESOURCE(lpCaption
))
1267 RtlCreateUnicodeStringFromAsciiz(&captionW
, (PCSZ
)lpCaption
);
1269 captionW
.Buffer
= (LPWSTR
)lpCaption
;
1271 msgboxW
.cbSize
= sizeof(msgboxW
);
1272 msgboxW
.hwndOwner
= hWnd
;
1273 msgboxW
.hInstance
= 0;
1274 msgboxW
.lpszText
= textW
.Buffer
;
1275 msgboxW
.lpszCaption
= captionW
.Buffer
;
1276 msgboxW
.dwStyle
= uType
;
1277 msgboxW
.lpszIcon
= NULL
;
1278 msgboxW
.dwContextHelpId
= 0;
1279 msgboxW
.lpfnMsgBoxCallback
= NULL
;
1280 msgboxW
.dwLanguageId
= wLanguageId
;
1282 ret
= MessageBoxTimeoutIndirectW(&msgboxW
, (UINT
)dwTimeout
);
1284 if (!IS_INTRESOURCE(textW
.Buffer
))
1285 RtlFreeUnicodeString(&textW
);
1287 if (!IS_INTRESOURCE(captionW
.Buffer
))
1288 RtlFreeUnicodeString(&captionW
);
1301 IN LPCWSTR lpCaption
,
1303 IN WORD wLanguageId
,
1306 MSGBOXPARAMSW msgbox
;
1308 msgbox
.cbSize
= sizeof(msgbox
);
1309 msgbox
.hwndOwner
= hWnd
;
1310 msgbox
.hInstance
= 0;
1311 msgbox
.lpszText
= lpText
;
1312 msgbox
.lpszCaption
= lpCaption
;
1313 msgbox
.dwStyle
= uType
;
1314 msgbox
.lpszIcon
= NULL
;
1315 msgbox
.dwContextHelpId
= 0;
1316 msgbox
.lpfnMsgBoxCallback
= NULL
;
1317 msgbox
.dwLanguageId
= wLanguageId
;
1319 return MessageBoxTimeoutIndirectW(&msgbox
, (UINT
)dwTimeout
);
1331 return NtUserxMessageBeep(uType
);
1338 * See: https://msdn.microsoft.com/en-us/library/windows/desktop/dn910915(v=vs.85).aspx
1339 * and: http://undoc.airesoft.co.uk/user32.dll/MB_GetString.php
1340 * for more information.
1347 static BOOL bCached
= FALSE
;
1348 static MBSTRING MBStrings
[MAX_MB_STRINGS
]; // FIXME: Use gpsi->MBStrings when this is loaded by Win32k!
1351 // FIXME - TODO: The gpsi->MBStrings[] array should be loaded by win32k!
1353 ASSERT(IDCONTINUE
<= MAX_MB_STRINGS
);
1357 for (i
= 0; i
< MAX_MB_STRINGS
; ++i
)
1359 /*gpsi->*/MBStrings
[i
].uID
= IDOK
+ i
;
1360 /*gpsi->*/MBStrings
[i
].uStr
= IDS_OK
+ i
; // See user32/include/resource.h
1361 LoadStringW(User32Instance
,
1362 /*gpsi->*/MBStrings
[i
].uStr
,
1363 /*gpsi->*/MBStrings
[i
].szName
,
1364 ARRAYSIZE(/*gpsi->*/MBStrings
[i
].szName
));
1370 * The allowable IDs are between "IDOK - 1" (0) and "IDCONTINUE - 1" (10) inclusive.
1371 * See psdk/winuser.h and user32/include/resource.h .
1373 if (wBtn
> IDCONTINUE
- 1)
1376 return /*gpsi->*/MBStrings
[wBtn
].szName
;