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)
26 * 2003/07/28 Added some NT features
27 * 2003/07/27 Code ported from wine
28 * 09-05-2001 CSH Created
33 WINE_DEFAULT_DEBUG_CHANNEL(user32
);
35 /* DEFINES *******************************************************************/
37 #define MSGBOX_IDICON (1088)
38 #define MSGBOX_IDTEXT (0xffff)
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)
47 /* MessageBox metrics */
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)
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)
63 /* MessageBox button helpers */
65 #define DECLARE_MB_1(_btn0) \
66 { 1, { ID##_btn0, 0, 0 }, { IDS_##_btn0, 0, 0 } }
68 #define DECLARE_MB_2(_btn0, _btn1) \
69 { 2, { ID##_btn0, ID##_btn1, 0 }, { IDS_##_btn0, IDS_##_btn1, 0 } }
71 #define DECLARE_MB_3(_btn0, _btn1, _btn2) \
72 { 3, { ID##_btn0, ID##_btn1, ID##_btn2 }, { IDS_##_btn0, IDS_##_btn1, IDS_##_btn2 } }
74 typedef struct _MSGBTNINFO
77 INT btnIdx
[MSGBOXEX_MAXBTNS
];
78 UINT btnIds
[MSGBOXEX_MAXBTNS
];
79 } MSGBTNINFO
, *PMSGBTNINFO
;
81 /* Default MessageBox buttons */
82 static const MSGBTNINFO MsgBtnInfo
[] =
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
),
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
)
100 /* INTERNAL FUNCTIONS ********************************************************/
102 static VOID
MessageBoxTextToClipboard(HWND DialogWindow
)
106 int cchTotal
, cchTitle
, cchText
, cchButton
, i
, n
, cchBuffer
;
107 LPWSTR pszBuffer
, pszBufferPos
, pMessageBoxText
, pszTitle
, pszText
, pszButton
;
108 WCHAR szButton
[MSGBOXEX_MAXBTNSTR
];
111 static const WCHAR szLine
[] = L
"---------------------------\r\n";
113 mbd
= (PMSGBOXDATA
)GetPropW(DialogWindow
, L
"ROS_MSGBOX");
114 hwndText
= GetDlgItem(DialogWindow
, MSGBOX_IDTEXT
);
115 cchTitle
= GetWindowTextLengthW(DialogWindow
) + 1;
116 cchText
= GetWindowTextLengthW(hwndText
) + 1;
121 pMessageBoxText
= (LPWSTR
)RtlAllocateHeap(GetProcessHeap(), 0, (cchTitle
+ cchText
) * sizeof(WCHAR
));
123 if (pMessageBoxText
== NULL
)
125 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText
);
129 pszTitle
= pMessageBoxText
;
130 pszText
= pMessageBoxText
+ cchTitle
;
132 if (GetWindowTextW(DialogWindow
, pszTitle
, cchTitle
) == 0 ||
133 GetWindowTextW(hwndText
, pszText
, cchText
) == 0)
135 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText
);
140 * Calculate the total buffer size.
142 cchTotal
= 6 + cchTitle
+ cchText
+ (lstrlenW(szLine
) * 4) + (mbd
->dwButtons
* MSGBOXEX_MAXBTNSTR
+ 3);
144 hGlobal
= GlobalAlloc(GHND
, cchTotal
* sizeof(WCHAR
));
146 pszBuffer
= (LPWSTR
)GlobalLock(hGlobal
);
148 if (pszBuffer
== NULL
)
150 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText
);
156 * First format title and text.
163 cchBuffer
= wsprintfW(pszBuffer
, L
"%s%s\r\n%s%s\r\n%s", szLine
, pszTitle
, szLine
, pszText
, szLine
);
164 pszBufferPos
= pszBuffer
+ cchBuffer
;
166 for (i
= 0; i
< mbd
->dwButtons
; i
++)
168 GetDlgItemTextW(DialogWindow
, mbd
->pidButton
[i
], szButton
, MSGBOXEX_MAXBTNSTR
);
170 cchButton
= strlenW(szButton
);
171 pszButton
= szButton
;
173 /* Skip '&' character. */
174 if (szButton
[0] == '&')
176 pszButton
= pszButton
+ 1;
177 cchButton
= cchButton
- 1;
180 for (n
= 0; n
< cchButton
; n
++)
181 *(pszBufferPos
++) = pszButton
[n
];
184 *(pszBufferPos
++) = L
' ';
185 *(pszBufferPos
++) = L
' ';
186 *(pszBufferPos
++) = L
' ';
189 wsprintfW(pszBufferPos
, L
"\r\n%s", szLine
);
191 GlobalUnlock(hGlobal
);
193 if (OpenClipboard(DialogWindow
))
196 SetClipboardData(CF_UNICODETEXT
, hGlobal
);
203 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText
);
206 static INT_PTR CALLBACK
MessageBoxProc(
207 HWND hwnd
, UINT message
,
208 WPARAM wParam
, LPARAM lParam
)
219 mbd
= (PMSGBOXDATA
)lParam
;
221 SetWindowLongPtrW(hwnd
, GWLP_USERDATA
, (LONG_PTR
)mbd
);
222 NtUserxSetMessageBox(hwnd
);
224 if (!GetPropW(hwnd
, L
"ROS_MSGBOX"))
226 SetPropW(hwnd
, L
"ROS_MSGBOX", (HANDLE
)lParam
);
228 if (mbd
->mbp
.dwContextHelpId
)
229 SetWindowContextHelpId(hwnd
, mbd
->mbp
.dwContextHelpId
);
231 if (mbd
->mbp
.lpszIcon
)
233 SendDlgItemMessageW(hwnd
, MSGBOX_IDICON
, STM_SETICON
, (WPARAM
)(HICON
)mbd
->mbp
.lpszIcon
, 0);
234 Alert
= ALERT_SYSTEM_WARNING
;
236 else // Setup the rest of the alerts.
238 switch (mbd
->mbp
.dwStyle
& MB_ICONMASK
)
241 Alert
= ALERT_SYSTEM_WARNING
;
244 Alert
= ALERT_SYSTEM_ERROR
;
246 case MB_ICONQUESTION
:
247 Alert
= ALERT_SYSTEM_QUERY
;
250 Alert
= ALERT_SYSTEM_INFORMATIONAL
;
254 /* Send out the alert notifications. */
255 NotifyWinEvent(EVENT_SYSTEM_ALERT
, hwnd
, OBJID_ALERT
, Alert
);
257 switch (mbd
->mbp
.dwStyle
& MB_TYPEMASK
)
259 case MB_ABORTRETRYIGNORE
:
261 RemoveMenu(GetSystemMenu(hwnd
, FALSE
), SC_CLOSE
, MF_BYCOMMAND
);
264 ASSERT(mbd
->uDefButton
< mbd
->dwButtons
);
265 SetFocus(GetDlgItem(hwnd
, mbd
->pidButton
[mbd
->uDefButton
]));
266 if (mbd
->dwTimeout
&& (mbd
->dwTimeout
!= (UINT
)-1))
267 SetTimer(hwnd
, 0, mbd
->dwTimeout
, NULL
);
273 switch (LOWORD(wParam
))
284 EndDialog(hwnd
, wParam
);
287 /* send WM_HELP message to messagebox window */
288 hi
.cbSize
= sizeof(hi
);
289 hi
.iContextType
= HELPINFO_WINDOW
;
290 hi
.iCtrlId
= LOWORD(wParam
);
291 hi
.hItemHandle
= (HANDLE
)lParam
;
293 GetCursorPos(&hi
.MousePos
);
294 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
300 MessageBoxTextToClipboard(hwnd
);
305 mbd
= (PMSGBOXDATA
)GetPropW(hwnd
, L
"ROS_MSGBOX");
308 memcpy(&hi
, (void *)lParam
, sizeof(hi
));
309 hi
.dwContextId
= GetWindowContextHelpId(hwnd
);
311 if (mbd
->mbp
.lpfnMsgBoxCallback
)
313 mbd
->mbp
.lpfnMsgBoxCallback(&hi
);
317 hwndOwner
= GetWindow(hwnd
, GW_OWNER
);
319 SendMessageW(hwndOwner
, WM_HELP
, 0, (LPARAM
)&hi
);
326 mbd
= (PMSGBOXDATA
)GetPropW(hwnd
, L
"ROS_MSGBOX");
329 switch (mbd
->mbp
.dwStyle
& MB_TYPEMASK
)
331 case MB_ABORTRETRYIGNORE
:
335 EndDialog(hwnd
, IDCANCEL
);
342 EndDialog(hwnd
, IDTIMEOUT
);
350 MessageBoxTimeoutIndirectW(
351 CONST MSGBOXPARAMSW
*lpMsgBoxParams
, UINT dwTimeout
)
355 DLGITEMTEMPLATE
*iico
, *itxt
;
356 NONCLIENTMETRICSW nclm
;
359 LPCWSTR caption
, text
;
360 HFONT hFont
, hOldFont
;
365 int bufsize
, ret
, caplen
, textlen
, i
, btnleft
, btntop
, lmargin
;
367 LPCWSTR ButtonText
[MSGBOXEX_MAXBTNS
];
368 int ButtonLen
[MSGBOXEX_MAXBTNS
];
369 DLGITEMTEMPLATE
*ibtn
[MSGBOXEX_MAXBTNS
];
370 RECT btnrect
, txtrect
, rc
;
374 ZeroMemory(&mbd
, sizeof(mbd
));
375 memcpy(&mbd
.mbp
, lpMsgBoxParams
, sizeof(mbd
.mbp
));
376 lpMsgBoxParams
= &mbd
.mbp
;
378 mbd
.wLanguageId
= (WORD
)lpMsgBoxParams
->dwLanguageId
; // FIXME!
379 mbd
.dwTimeout
= dwTimeout
;
381 /* Create the selected buttons; unknown types will fall back to MB_OK */
382 i
= (lpMsgBoxParams
->dwStyle
& MB_TYPEMASK
);
383 if (i
>= ARRAYSIZE(MsgBtnInfo
))
386 /* Get buttons IDs */
387 Buttons
= MsgBtnInfo
[i
];
389 /* Add the Help button */
390 if (lpMsgBoxParams
->dwStyle
& MB_HELP
)
392 Buttons
.btnIdx
[Buttons
.btnCnt
] = IDHELP
;
393 Buttons
.btnIds
[Buttons
.btnCnt
] = IDS_HELP
;
397 mbd
.pidButton
= Buttons
.btnIdx
;
398 mbd
.ppszButtonText
= ButtonText
;
399 mbd
.dwButtons
= Buttons
.btnCnt
;
401 mbd
.uDefButton
= ((lpMsgBoxParams
->dwStyle
& MB_DEFMASK
) >> 8);
402 /* Make the first button the default button if none other is */
403 if (mbd
.uDefButton
>= mbd
.dwButtons
)
408 if (!lpMsgBoxParams
->lpszCaption
)
410 /* No caption, use the default one */
411 caplen
= LoadStringW(User32Instance
, IDS_ERROR
, (LPWSTR
)&caption
, 0);
413 else if (IS_INTRESOURCE(lpMsgBoxParams
->lpszCaption
))
415 /* User-defined resource string */
416 caplen
= LoadStringW(lpMsgBoxParams
->hInstance
,
417 PtrToUlong(lpMsgBoxParams
->lpszCaption
),
418 (LPWSTR
)&caption
, 0);
422 /* UNICODE string pointer */
423 caption
= lpMsgBoxParams
->lpszCaption
;
424 caplen
= strlenW(caption
);
427 if (!lpMsgBoxParams
->lpszText
)
429 /* No text, use blank */
433 else if (IS_INTRESOURCE(lpMsgBoxParams
->lpszText
))
435 /* User-defined resource string */
436 textlen
= LoadStringW(lpMsgBoxParams
->hInstance
,
437 PtrToUlong(lpMsgBoxParams
->lpszText
),
442 /* UNICODE string pointer */
443 text
= lpMsgBoxParams
->lpszText
;
444 textlen
= strlenW(text
);
447 switch (lpMsgBoxParams
->dwStyle
& MB_ICONMASK
)
449 case MB_ICONEXCLAMATION
: // case MB_ICONWARNING:
450 Icon
= LoadIconW(0, IDI_EXCLAMATIONW
);
451 MessageBeep(MB_ICONEXCLAMATION
);
453 case MB_ICONQUESTION
:
454 Icon
= LoadIconW(0, IDI_QUESTIONW
);
455 MessageBeep(MB_ICONQUESTION
);
457 case MB_ICONASTERISK
: // case MB_ICONINFORMATION:
458 Icon
= LoadIconW(0, IDI_ASTERISKW
);
459 MessageBeep(MB_ICONASTERISK
);
461 case MB_ICONHAND
: // case MB_ICONSTOP: case MB_ICONERROR:
462 Icon
= LoadIconW(0, IDI_HANDW
);
463 MessageBeep(MB_ICONHAND
);
466 Icon
= LoadIconW(lpMsgBoxParams
->hInstance
, lpMsgBoxParams
->lpszIcon
);
471 * By default, Windows 95/98/NT does not associate an icon
472 * to message boxes. So ReactOS should do the same.
479 /* Reuse the internal pointer! */
480 ((MSGBOXPARAMSW
*)lpMsgBoxParams
)->lpszIcon
= (LPCWSTR
)Icon
;
483 bufsize
= sizeof(DLGTEMPLATE
) +
484 2 * sizeof(WORD
) + /* menu and class */
485 (caplen
+ 1) * sizeof(WCHAR
) + /* title */
486 sizeof(WORD
); /* font height */
491 bufsize
= ALIGN_UP(bufsize
, DWORD
);
492 bufsize
+= sizeof(DLGITEMTEMPLATE
) +
498 bufsize
= ALIGN_UP(bufsize
, DWORD
);
499 bufsize
+= sizeof(DLGITEMTEMPLATE
) +
501 (textlen
+ 1) * sizeof(WCHAR
);
503 for (i
= 0; i
< mbd
.dwButtons
; i
++)
505 /* Get the default text of the buttons */
506 if (Buttons
.btnIds
[i
])
508 ButtonLen
[i
] = LoadStringW(User32Instance
,
510 (LPWSTR
)&mbd
.ppszButtonText
[i
], 0);
514 /* No text, use blank */
515 mbd
.ppszButtonText
[i
] = L
"";
519 /* Space for buttons */
520 bufsize
= ALIGN_UP(bufsize
, DWORD
);
521 bufsize
+= sizeof(DLGITEMTEMPLATE
) +
523 (ButtonLen
[i
] + 1) * sizeof(WCHAR
);
526 buf
= RtlAllocateHeap(GetProcessHeap(), 0, bufsize
);
532 nclm
.cbSize
= sizeof(nclm
);
533 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(nclm
), &nclm
, 0);
534 hFont
= CreateFontIndirectW(&nclm
.lfMessageFont
);
537 ERR("Cannot retrieve nclm.lfMessageFont!\n");
541 tpl
= (DLGTEMPLATE
*)buf
;
543 tpl
->style
= WS_CAPTION
| WS_POPUP
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_SYSMENU
|
544 DS_CENTER
| DS_SETFONT
| DS_MODALFRAME
| DS_NOIDLEMSG
;
545 tpl
->dwExtendedStyle
= WS_EX_DLGMODALFRAME
| WS_EX_WINDOWEDGE
| WS_EX_CONTROLPARENT
;
546 if (lpMsgBoxParams
->dwStyle
& MB_TOPMOST
)
547 tpl
->dwExtendedStyle
|= WS_EX_TOPMOST
;
548 if (lpMsgBoxParams
->dwStyle
& MB_RIGHT
)
549 tpl
->dwExtendedStyle
|= WS_EX_RIGHT
;
552 tpl
->cdit
= mbd
.dwButtons
+ (Icon
? 1 : 0) + 1;
554 dest
= (BYTE
*)(tpl
+ 1);
556 *(DWORD
*)dest
= 0; /* no menu and use default window class */
557 dest
+= 2 * sizeof(WORD
);
558 memcpy(dest
, caption
, caplen
* sizeof(WCHAR
));
559 dest
+= caplen
* sizeof(WCHAR
);
560 *(WCHAR
*)dest
= L
'\0';
561 dest
+= sizeof(WCHAR
);
564 * A font point size (height) of 0x7FFF means that we use
565 * the message box font (NONCLIENTMETRICSW.lfMessageFont).
567 *(WORD
*)dest
= 0x7FFF;
568 dest
+= sizeof(WORD
);
573 dest
= ALIGN_UP_POINTER(dest
, DWORD
);
574 iico
= (DLGITEMTEMPLATE
*)dest
;
575 iico
->style
= WS_CHILD
| WS_VISIBLE
| SS_ICON
;
576 iico
->dwExtendedStyle
= 0;
577 iico
->id
= MSGBOX_IDICON
;
579 dest
+= sizeof(DLGITEMTEMPLATE
);
580 *(WORD
*)dest
= 0xFFFF;
581 dest
+= sizeof(WORD
);
582 *(WORD
*)dest
= 0x0082; /* static control */
583 dest
+= sizeof(WORD
);
584 *(WORD
*)dest
= 0xFFFF;
585 dest
+= sizeof(WORD
);
587 dest
+= sizeof(WCHAR
);
589 dest
+= sizeof(WORD
);
592 /* Create static for text */
593 dest
= ALIGN_UP_POINTER(dest
, DWORD
);
594 itxt
= (DLGITEMTEMPLATE
*)dest
;
595 itxt
->style
= WS_CHILD
| WS_VISIBLE
| SS_NOPREFIX
;
596 if (lpMsgBoxParams
->dwStyle
& MB_RIGHT
)
597 itxt
->style
|= SS_RIGHT
;
599 itxt
->style
|= SS_LEFT
;
600 itxt
->dwExtendedStyle
= 0;
601 itxt
->id
= MSGBOX_IDTEXT
;
602 dest
+= sizeof(DLGITEMTEMPLATE
);
603 *(WORD
*)dest
= 0xFFFF;
604 dest
+= sizeof(WORD
);
605 *(WORD
*)dest
= 0x0082; /* static control */
606 dest
+= sizeof(WORD
);
607 memcpy(dest
, text
, textlen
* sizeof(WCHAR
));
608 dest
+= textlen
* sizeof(WCHAR
);
610 dest
+= sizeof(WCHAR
);
612 dest
+= sizeof(WORD
);
615 hDC
= GetDCEx(hDCWnd
, NULL
, DCX_WINDOW
| DCX_CACHE
);
618 /* Retry with the DC of the owner window */
619 hDCWnd
= lpMsgBoxParams
->hwndOwner
;
620 hDC
= GetDCEx(hDCWnd
, NULL
, DCX_WINDOW
| DCX_CACHE
);
624 ERR("GetDCEx() failed, bail out!\n");
627 hOldFont
= SelectObject(hDC
, hFont
);
629 units
.cx
= GdiGetCharDimensions(hDC
, NULL
, &units
.cy
);
633 ERR("GdiGetCharDimensions() failed, falling back to default values!\n");
634 defUnits
= GetDialogBaseUnits();
635 units
.cx
= LOWORD(defUnits
);
636 units
.cy
= HIWORD(defUnits
);
642 btnrect
.left
= btnrect
.top
= 0;
644 /* Make the first button the default button if none other is */
645 if (mbd
.uDefButton
>= mbd
.dwButtons
)
648 for (i
= 0; i
< mbd
.dwButtons
; i
++)
650 dest
= ALIGN_UP_POINTER(dest
, DWORD
);
651 ibtn
[i
] = (DLGITEMTEMPLATE
*)dest
;
653 ibtn
[i
]->style
= WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
;
654 if (i
== mbd
.uDefButton
)
655 ibtn
[i
]->style
|= BS_DEFPUSHBUTTON
;
657 ibtn
[i
]->style
|= BS_PUSHBUTTON
;
659 ibtn
[i
]->dwExtendedStyle
= 0;
660 ibtn
[i
]->id
= mbd
.pidButton
[i
];
661 dest
+= sizeof(DLGITEMTEMPLATE
);
662 *(WORD
*)dest
= 0xFFFF;
663 dest
+= sizeof(WORD
);
664 *(WORD
*)dest
= 0x0080; /* button control */
665 dest
+= sizeof(WORD
);
666 memcpy(dest
, mbd
.ppszButtonText
[i
], ButtonLen
[i
] * sizeof(WCHAR
));
667 dest
+= ButtonLen
[i
] * sizeof(WCHAR
);
669 dest
+= sizeof(WORD
);
671 dest
+= sizeof(WORD
);
673 // btnrect.right = btnrect.bottom = 0; // FIXME: Is it needed??
674 DrawTextW(hDC
, mbd
.ppszButtonText
[i
], ButtonLen
[i
], &btnrect
,
675 DT_LEFT
| DT_SINGLELINE
| DT_CALCRECT
);
676 btnsize
.cx
= max(btnsize
.cx
, btnrect
.right
);
677 btnsize
.cy
= max(btnsize
.cy
, btnrect
.bottom
);
680 /* calculate position and size of controls */
681 txtrect
.right
= GetSystemMetrics(SM_CXSCREEN
) / 5 * 4;
683 txtrect
.right
-= GetSystemMetrics(SM_CXICON
) + MSGBOXEX_SPACING
;
684 txtrect
.top
= txtrect
.left
= txtrect
.bottom
= 0;
687 DrawTextW(hDC
, text
, textlen
, &txtrect
,
688 DT_LEFT
| DT_NOPREFIX
| DT_WORDBREAK
| DT_EXPANDTABS
| DT_EXTERNALLEADING
| DT_EDITCONTROL
| DT_CALCRECT
);
692 txtrect
.right
= txtrect
.left
+ 1;
693 txtrect
.bottom
= txtrect
.top
+ 1;
698 SelectObject(hDC
, hOldFont
);
700 ReleaseDC(hDCWnd
, hDC
);
705 /* calculate position and size of the icon */
706 rc
.left
= rc
.bottom
= rc
.right
= 0;
711 rc
.right
= GetSystemMetrics(SM_CXICON
);
712 rc
.bottom
= GetSystemMetrics(SM_CYICON
);
713 #ifdef MSGBOX_ICONVCENTER
714 rc
.top
= MSGBOXEX_MARGIN
+ (max(txtrect
.bottom
, rc
.bottom
) / 2) - (GetSystemMetrics(SM_CYICON
) / 2);
715 rc
.top
= max(MSGBOXEX_SPACING
, rc
.top
);
717 rc
.top
= MSGBOXEX_MARGIN
;
719 btnleft
= (mbd
.dwButtons
* (btnsize
.cx
+ MSGBOXEX_BUTTONSPACING
)) - MSGBOXEX_BUTTONSPACING
;
720 if (btnleft
> txtrect
.right
+ rc
.right
+ MSGBOXEX_SPACING
)
722 #ifdef MSGBOX_TEXTHCENTER
723 lmargin
= MSGBOXEX_MARGIN
+ ((btnleft
- txtrect
.right
- rc
.right
- MSGBOXEX_SPACING
) / 2);
725 lmargin
= MSGBOXEX_MARGIN
;
727 btnleft
= MSGBOXEX_MARGIN
;
731 lmargin
= MSGBOXEX_MARGIN
;
732 btnleft
= MSGBOXEX_MARGIN
+ ((txtrect
.right
+ rc
.right
+ MSGBOXEX_SPACING
) / 2) - (btnleft
/ 2);
735 iico
->x
= RESCALE_X(rc
.left
, units
);
736 iico
->y
= RESCALE_Y(rc
.top
, units
);
737 iico
->cx
= RESCALE_X(rc
.right
, units
);
738 iico
->cy
= RESCALE_Y(rc
.bottom
, units
);
739 btntop
= rc
.top
+ rc
.bottom
+ MSGBOXEX_SPACING
;
740 rc
.left
+= rc
.right
+ MSGBOXEX_SPACING
;
744 btnleft
= (mbd
.dwButtons
* (btnsize
.cx
+ MSGBOXEX_BUTTONSPACING
)) - MSGBOXEX_BUTTONSPACING
;
745 if (btnleft
> txtrect
.right
)
747 #ifdef MSGBOX_TEXTHCENTER
748 lmargin
= MSGBOXEX_MARGIN
+ ((btnleft
- txtrect
.right
) / 2);
750 lmargin
= MSGBOXEX_MARGIN
;
752 btnleft
= MSGBOXEX_MARGIN
;
756 lmargin
= MSGBOXEX_MARGIN
;
757 btnleft
= MSGBOXEX_MARGIN
+ (txtrect
.right
/ 2) - (btnleft
/ 2);
761 /* calculate position of the text */
762 rc
.top
= MSGBOXEX_MARGIN
+ (rc
.bottom
/ 2) - (txtrect
.bottom
/ 2);
763 rc
.top
= max(rc
.top
, MSGBOXEX_MARGIN
);
764 /* calculate position of the buttons */
765 btntop
= max(rc
.top
+ txtrect
.bottom
+ MSGBOXEX_SPACING
, btntop
);
766 for (i
= 0; i
< mbd
.dwButtons
; i
++)
768 ibtn
[i
]->x
= RESCALE_X(btnleft
, units
);
769 ibtn
[i
]->y
= RESCALE_Y(btntop
, units
);
770 ibtn
[i
]->cx
= RESCALE_X(btnsize
.cx
, units
);
771 ibtn
[i
]->cy
= RESCALE_Y(btnsize
.cy
, units
);
772 btnleft
+= btnsize
.cx
+ MSGBOXEX_BUTTONSPACING
;
774 /* calculate size and position of the messagebox window */
775 btnleft
= max(btnleft
- MSGBOXEX_BUTTONSPACING
, rc
.left
+ txtrect
.right
);
776 btnleft
+= MSGBOXEX_MARGIN
;
777 btntop
+= btnsize
.cy
+ MSGBOXEX_MARGIN
;
778 /* set size and position of the message static */
779 itxt
->x
= RESCALE_X(rc
.left
, units
);
780 itxt
->y
= RESCALE_Y(rc
.top
, units
);
781 itxt
->cx
= RESCALE_X(btnleft
- rc
.left
- MSGBOXEX_MARGIN
, units
);
782 itxt
->cy
= RESCALE_Y(txtrect
.bottom
, units
);
783 /* set size of the window */
784 tpl
->cx
= RESCALE_X(btnleft
, units
);
785 tpl
->cy
= RESCALE_Y(btntop
, units
);
787 /* Finally show the messagebox */
788 ret
= DialogBoxIndirectParamW(lpMsgBoxParams
->hInstance
, tpl
,
789 lpMsgBoxParams
->hwndOwner
,
790 MessageBoxProc
, (LPARAM
)&mbd
);
793 RtlFreeHeap(GetProcessHeap(), 0, buf
);
798 /* FUNCTIONS *****************************************************************/
811 return MessageBoxExA(hWnd
, lpText
, lpCaption
, uType
, LANG_NEUTRAL
);
822 IN LPCWSTR lpCaption
,
825 return MessageBoxExW(hWnd
, lpText
, lpCaption
, uType
, LANG_NEUTRAL
);
841 MSGBOXPARAMSA msgbox
;
843 msgbox
.cbSize
= sizeof(msgbox
);
844 msgbox
.hwndOwner
= hWnd
;
845 msgbox
.hInstance
= 0;
846 msgbox
.lpszText
= lpText
;
847 msgbox
.lpszCaption
= lpCaption
;
848 msgbox
.dwStyle
= uType
;
849 msgbox
.lpszIcon
= NULL
;
850 msgbox
.dwContextHelpId
= 0;
851 msgbox
.lpfnMsgBoxCallback
= NULL
;
852 msgbox
.dwLanguageId
= wLanguageId
;
854 return MessageBoxIndirectA(&msgbox
);
865 IN LPCWSTR lpCaption
,
869 MSGBOXPARAMSW msgbox
;
871 msgbox
.cbSize
= sizeof(msgbox
);
872 msgbox
.hwndOwner
= hWnd
;
873 msgbox
.hInstance
= 0;
874 msgbox
.lpszText
= lpText
;
875 msgbox
.lpszCaption
= lpCaption
;
876 msgbox
.dwStyle
= uType
;
877 msgbox
.lpszIcon
= NULL
;
878 msgbox
.dwContextHelpId
= 0;
879 msgbox
.lpfnMsgBoxCallback
= NULL
;
880 msgbox
.dwLanguageId
= wLanguageId
;
882 return MessageBoxTimeoutIndirectW(&msgbox
, (UINT
)-1);
892 IN CONST MSGBOXPARAMSA
* lpMsgBoxParams
)
894 MSGBOXPARAMSW msgboxW
;
895 UNICODE_STRING textW
, captionW
, iconW
;
898 if (!IS_INTRESOURCE(lpMsgBoxParams
->lpszText
))
900 RtlCreateUnicodeStringFromAsciiz(&textW
, (PCSZ
)lpMsgBoxParams
->lpszText
);
902 * UNICODE_STRING objects are always allocated with an extra byte so you
903 * can null-term if you want
905 textW
.Buffer
[textW
.Length
/ sizeof(WCHAR
)] = L
'\0';
908 textW
.Buffer
= (LPWSTR
)lpMsgBoxParams
->lpszText
;
910 if (!IS_INTRESOURCE(lpMsgBoxParams
->lpszCaption
))
912 RtlCreateUnicodeStringFromAsciiz(&captionW
, (PCSZ
)lpMsgBoxParams
->lpszCaption
);
914 * UNICODE_STRING objects are always allocated with an extra byte so you
915 * can null-term if you want
917 captionW
.Buffer
[captionW
.Length
/ sizeof(WCHAR
)] = L
'\0';
920 captionW
.Buffer
= (LPWSTR
)lpMsgBoxParams
->lpszCaption
;
922 if (lpMsgBoxParams
->dwStyle
& MB_USERICON
)
924 if (!IS_INTRESOURCE(lpMsgBoxParams
->lpszIcon
))
926 RtlCreateUnicodeStringFromAsciiz(&iconW
, (PCSZ
)lpMsgBoxParams
->lpszIcon
);
928 * UNICODE_STRING objects are always allocated with an extra byte so you
929 * can null-term if you want
931 iconW
.Buffer
[iconW
.Length
/ sizeof(WCHAR
)] = L
'\0';
934 iconW
.Buffer
= (LPWSTR
)lpMsgBoxParams
->lpszIcon
;
939 msgboxW
.cbSize
= sizeof(msgboxW
);
940 msgboxW
.hwndOwner
= lpMsgBoxParams
->hwndOwner
;
941 msgboxW
.hInstance
= lpMsgBoxParams
->hInstance
;
942 msgboxW
.lpszText
= textW
.Buffer
;
943 msgboxW
.lpszCaption
= captionW
.Buffer
;
944 msgboxW
.dwStyle
= lpMsgBoxParams
->dwStyle
;
945 msgboxW
.lpszIcon
= iconW
.Buffer
;
946 msgboxW
.dwContextHelpId
= lpMsgBoxParams
->dwContextHelpId
;
947 msgboxW
.lpfnMsgBoxCallback
= lpMsgBoxParams
->lpfnMsgBoxCallback
;
948 msgboxW
.dwLanguageId
= lpMsgBoxParams
->dwLanguageId
;
950 ret
= MessageBoxTimeoutIndirectW(&msgboxW
, (UINT
)-1);
952 if (!IS_INTRESOURCE(lpMsgBoxParams
->lpszText
))
953 RtlFreeUnicodeString(&textW
);
955 if (!IS_INTRESOURCE(lpMsgBoxParams
->lpszCaption
))
956 RtlFreeUnicodeString(&captionW
);
958 if ((lpMsgBoxParams
->dwStyle
& MB_USERICON
) && !IS_INTRESOURCE(iconW
.Buffer
))
959 RtlFreeUnicodeString(&iconW
);
970 IN CONST MSGBOXPARAMSW
* lpMsgBoxParams
)
972 return MessageBoxTimeoutIndirectW(lpMsgBoxParams
, (UINT
)-1);
989 MSGBOXPARAMSW msgboxW
;
990 UNICODE_STRING textW
, captionW
;
993 if (!IS_INTRESOURCE(lpText
))
994 RtlCreateUnicodeStringFromAsciiz(&textW
, (PCSZ
)lpText
);
996 textW
.Buffer
= (LPWSTR
)lpText
;
998 if (!IS_INTRESOURCE(lpCaption
))
999 RtlCreateUnicodeStringFromAsciiz(&captionW
, (PCSZ
)lpCaption
);
1001 captionW
.Buffer
= (LPWSTR
)lpCaption
;
1003 msgboxW
.cbSize
= sizeof(msgboxW
);
1004 msgboxW
.hwndOwner
= hWnd
;
1005 msgboxW
.hInstance
= 0;
1006 msgboxW
.lpszText
= textW
.Buffer
;
1007 msgboxW
.lpszCaption
= captionW
.Buffer
;
1008 msgboxW
.dwStyle
= uType
;
1009 msgboxW
.lpszIcon
= NULL
;
1010 msgboxW
.dwContextHelpId
= 0;
1011 msgboxW
.lpfnMsgBoxCallback
= NULL
;
1012 msgboxW
.dwLanguageId
= wLanguageId
;
1014 ret
= MessageBoxTimeoutIndirectW(&msgboxW
, (UINT
)dwTimeout
);
1016 if (!IS_INTRESOURCE(textW
.Buffer
))
1017 RtlFreeUnicodeString(&textW
);
1019 if (!IS_INTRESOURCE(captionW
.Buffer
))
1020 RtlFreeUnicodeString(&captionW
);
1033 IN LPCWSTR lpCaption
,
1035 IN WORD wLanguageId
,
1038 MSGBOXPARAMSW msgbox
;
1040 msgbox
.cbSize
= sizeof(msgbox
);
1041 msgbox
.hwndOwner
= hWnd
;
1042 msgbox
.hInstance
= 0;
1043 msgbox
.lpszText
= lpText
;
1044 msgbox
.lpszCaption
= lpCaption
;
1045 msgbox
.dwStyle
= uType
;
1046 msgbox
.lpszIcon
= NULL
;
1047 msgbox
.dwContextHelpId
= 0;
1048 msgbox
.lpfnMsgBoxCallback
= NULL
;
1049 msgbox
.dwLanguageId
= wLanguageId
;
1051 return MessageBoxTimeoutIndirectW(&msgbox
, (UINT
)dwTimeout
);
1060 SoftModalMessageBox(DWORD Unknown0
)
1072 MessageBeep(IN UINT uType
)
1074 return NtUserxMessageBeep(uType
);
1081 * See: https://msdn.microsoft.com/en-us/library/windows/desktop/dn910915(v=vs.85).aspx
1082 * and: http://undoc.airesoft.co.uk/user32.dll/MB_GetString.php
1083 * for more information.
1087 MB_GetString(IN UINT wBtn
)
1089 LPCWSTR btnStr
= NULL
;
1092 * The allowable IDs are between "IDOK - 1" (0) and "IDCONTINUE - 1" (10) inclusive.
1093 * See psdk/winuser.h and user32/include/resource.h .
1095 if (wBtn
> IDCONTINUE
- 1)
1098 wBtn
+= 800; // See user32/include/resource.h
1099 LoadStringW(User32Instance
, wBtn
, (LPWSTR
)&btnStr
, 0);