658ba763b4a5fae1b21d44b5076a39da89b28f07
[reactos.git] / win32ss / user / user32 / windows / messagebox.c
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 * Hermes Belusca-Maito
26 * UPDATE HISTORY:
27 * 2003/07/28 Added some NT features
28 * 2003/07/27 Code ported from wine
29 * 09-05-2001 CSH Created
30 */
31
32 #include <user32.h>
33 #include <ndk/exfuncs.h>
34
35 #include <ntstrsafe.h>
36
37 WINE_DEFAULT_DEBUG_CHANNEL(user32);
38
39 /* DEFINES *******************************************************************/
40
41 #define MSGBOX_IDICON (1088)
42 #define MSGBOX_IDTEXT (0xffff)
43
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)
49
50
51 /* MessageBox metrics */
52
53 #define BTN_CX (75)
54 #define BTN_CY (23)
55
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)
61
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)
65
66
67 /* MessageBox button helpers */
68
69 #define DECLARE_MB_1(_btn0) \
70 { 1, { ID##_btn0, 0, 0 }, { IDS_##_btn0, 0, 0 } }
71
72 #define DECLARE_MB_2(_btn0, _btn1) \
73 { 2, { ID##_btn0, ID##_btn1, 0 }, { IDS_##_btn0, IDS_##_btn1, 0 } }
74
75 #define DECLARE_MB_3(_btn0, _btn1, _btn2) \
76 { 3, { ID##_btn0, ID##_btn1, ID##_btn2 }, { IDS_##_btn0, IDS_##_btn1, IDS_##_btn2 } }
77
78 typedef struct _MSGBTNINFO
79 {
80 DWORD btnCnt;
81 INT btnIdx[MSGBOXEX_MAXBTNS];
82 UINT btnIds[MSGBOXEX_MAXBTNS];
83 } MSGBTNINFO, *PMSGBTNINFO;
84
85 /* Default MessageBox buttons */
86 static const MSGBTNINFO MsgBtnInfo[] =
87 {
88 /* MB_OK (0) */
89 DECLARE_MB_1(OK),
90 /* MB_OKCANCEL (1) */
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),
96 /* MB_YESNO (4) */
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)
102 };
103
104
105 /* INTERNAL FUNCTIONS ********************************************************/
106
107 static UINT
108 LoadAllocStringW(
109 IN HINSTANCE hInstance OPTIONAL,
110 IN UINT uID,
111 OUT PWSTR* pString,
112 IN PCWSTR pDefaultString OPTIONAL)
113 {
114 UINT Length;
115 PCWSTR pStr;
116
117 /* Try to load the string from the resource */
118 Length = LoadStringW(hInstance, uID, (LPWSTR)&pStr, 0);
119 if (Length == 0)
120 {
121 /* If the resource string was not found, use the fallback default one */
122
123 if (!pDefaultString)
124 {
125 /* None was specified, return NULL */
126 *pString = NULL;
127 return 0;
128 }
129
130 pStr = pDefaultString;
131 Length = wcslen(pStr);
132 }
133
134 /* Allocate a new buffer, adding a NULL-terminator */
135 *pString = RtlAllocateHeap(RtlGetProcessHeap(), 0, (Length + 1) * sizeof(WCHAR));
136 if (!*pString)
137 return 0;
138
139 /* Copy the string, NULL-terminated */
140 RtlStringCchCopyNW(*pString, Length + 1, pStr, Length);
141 return Length;
142 }
143
144 static VOID MessageBoxTextToClipboard(HWND DialogWindow)
145 {
146 HWND hwndText;
147 PMSGBOXDATA mbd;
148 int cchTotal, cchTitle, cchText, cchButton, i, n, cchBuffer;
149 LPWSTR pszBuffer, pszBufferPos, pMessageBoxText, pszTitle, pszText, pszButton;
150 WCHAR szButton[MSGBOXEX_MAXBTNSTR];
151 HGLOBAL hGlobal;
152
153 static const WCHAR szLine[] = L"---------------------------\r\n";
154
155 mbd = (PMSGBOXDATA)GetPropW(DialogWindow, L"ROS_MSGBOX");
156 hwndText = GetDlgItem(DialogWindow, MSGBOX_IDTEXT);
157 cchTitle = GetWindowTextLengthW(DialogWindow) + 1;
158 cchText = GetWindowTextLengthW(hwndText) + 1;
159
160 if (!mbd)
161 return;
162
163 pMessageBoxText = (LPWSTR)RtlAllocateHeap(GetProcessHeap(), 0, (cchTitle + cchText) * sizeof(WCHAR));
164
165 if (pMessageBoxText == NULL)
166 {
167 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText);
168 return;
169 }
170
171 pszTitle = pMessageBoxText;
172 pszText = pMessageBoxText + cchTitle;
173
174 if (GetWindowTextW(DialogWindow, pszTitle, cchTitle) == 0 ||
175 GetWindowTextW(hwndText, pszText, cchText) == 0)
176 {
177 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText);
178 return;
179 }
180
181 /*
182 * Calculate the total buffer size.
183 */
184 cchTotal = 6 + cchTitle + cchText + (lstrlenW(szLine) * 4) + (mbd->dwButtons * MSGBOXEX_MAXBTNSTR + 3);
185
186 hGlobal = GlobalAlloc(GHND, cchTotal * sizeof(WCHAR));
187
188 pszBuffer = (LPWSTR)GlobalLock(hGlobal);
189
190 if (pszBuffer == NULL)
191 {
192 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText);
193 GlobalFree(hGlobal);
194 return;
195 }
196
197 /*
198 * First format title and text.
199 * ------------------
200 * Title
201 * ------------------
202 * Text
203 * ------------------
204 */
205 cchBuffer = wsprintfW(pszBuffer, L"%s%s\r\n%s%s\r\n%s", szLine, pszTitle, szLine, pszText, szLine);
206 pszBufferPos = pszBuffer + cchBuffer;
207
208 for (i = 0; i < mbd->dwButtons; i++)
209 {
210 GetDlgItemTextW(DialogWindow, mbd->pidButton[i], szButton, MSGBOXEX_MAXBTNSTR);
211
212 cchButton = strlenW(szButton);
213 pszButton = szButton;
214
215 /* Skip '&' character. */
216 if (szButton[0] == '&')
217 {
218 pszButton = pszButton + 1;
219 cchButton = cchButton - 1;
220 }
221
222 for (n = 0; n < cchButton; n++)
223 *(pszBufferPos++) = pszButton[n];
224
225 /* Add spaces. */
226 *(pszBufferPos++) = L' ';
227 *(pszBufferPos++) = L' ';
228 *(pszBufferPos++) = L' ';
229 }
230
231 wsprintfW(pszBufferPos, L"\r\n%s", szLine);
232
233 GlobalUnlock(hGlobal);
234
235 if (OpenClipboard(DialogWindow))
236 {
237 EmptyClipboard();
238 SetClipboardData(CF_UNICODETEXT, hGlobal);
239 CloseClipboard();
240 }
241 else
242 {
243 GlobalFree(hGlobal);
244 }
245 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText);
246 }
247
248 static INT_PTR CALLBACK MessageBoxProc(
249 HWND hwnd, UINT message,
250 WPARAM wParam, LPARAM lParam)
251 {
252 PMSGBOXDATA mbd;
253
254 switch (message)
255 {
256 case WM_INITDIALOG:
257 {
258 int Alert;
259
260 mbd = (PMSGBOXDATA)lParam;
261
262 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)mbd);
263 NtUserxSetMessageBox(hwnd);
264
265 if (!GetPropW(hwnd, L"ROS_MSGBOX"))
266 {
267 SetPropW(hwnd, L"ROS_MSGBOX", (HANDLE)lParam);
268
269 if (mbd->mbp.dwContextHelpId)
270 SetWindowContextHelpId(hwnd, mbd->mbp.dwContextHelpId);
271
272 if (mbd->mbp.lpszIcon)
273 {
274 SendDlgItemMessageW(hwnd, MSGBOX_IDICON, STM_SETICON, (WPARAM)(HICON)mbd->mbp.lpszIcon, 0);
275 Alert = ALERT_SYSTEM_WARNING;
276 }
277 else /* Setup the rest of the alerts */
278 {
279 switch (mbd->mbp.dwStyle & MB_ICONMASK)
280 {
281 case MB_ICONWARNING:
282 Alert = ALERT_SYSTEM_WARNING;
283 break;
284 case MB_ICONERROR:
285 Alert = ALERT_SYSTEM_ERROR;
286 break;
287 case MB_ICONQUESTION:
288 Alert = ALERT_SYSTEM_QUERY;
289 break;
290 default:
291 Alert = ALERT_SYSTEM_INFORMATIONAL;
292 /* Fall through */
293 }
294 }
295 /* Send out the alert notifications */
296 NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd, OBJID_ALERT, Alert);
297
298 /* Disable the Close menu button if no Cancel button is specified */
299 if (mbd->uCancelId == 0)
300 {
301 HMENU hSysMenu = GetSystemMenu(hwnd, FALSE);
302 if (hSysMenu)
303 DeleteMenu(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
304 }
305
306 /* Set the focus to the default button */
307 if (mbd->dwButtons > 0)
308 {
309 ASSERT(mbd->uDefButton < mbd->dwButtons);
310 SetFocus(GetDlgItem(hwnd, mbd->pidButton[mbd->uDefButton]));
311 }
312
313 /* Set up the window timer */
314 if (mbd->dwTimeout && (mbd->dwTimeout != (UINT)-1))
315 SetTimer(hwnd, 0, mbd->dwTimeout, NULL);
316 }
317 return FALSE;
318 }
319
320 case WM_COMMAND:
321 {
322 UINT i;
323 INT_PTR iCtrlId = LOWORD(wParam);
324
325 switch (iCtrlId)
326 {
327 /* Handle the default message-box buttons */
328 case IDOK:
329 case IDCANCEL:
330 /*
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.
335 */
336 if (iCtrlId == IDCANCEL)
337 {
338 mbd = (PMSGBOXDATA)GetPropW(hwnd, L"ROS_MSGBOX");
339 if (!mbd)
340 return FALSE; /* Ignore */
341
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;
347 }
348 if (!GetDlgItem(hwnd, iCtrlId))
349 return FALSE; /* Ignore */
350
351 /* Fall through */
352 case IDABORT:
353 case IDRETRY:
354 case IDIGNORE:
355 case IDYES:
356 case IDNO:
357 case IDTRYAGAIN:
358 case IDCONTINUE:
359 EndDialog(hwnd, iCtrlId);
360 return TRUE;
361
362 case IDCLOSE:
363 return FALSE; /* Ignore */
364
365 case IDHELP:
366 {
367 /* Send WM_HELP message to the message-box window */
368 HELPINFO hi;
369 hi.cbSize = sizeof(hi);
370 hi.iContextType = HELPINFO_WINDOW;
371 hi.iCtrlId = iCtrlId;
372 hi.hItemHandle = (HANDLE)lParam;
373 hi.dwContextId = 0;
374 GetCursorPos(&hi.MousePos);
375 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
376 return TRUE;
377 }
378
379 default:
380 break;
381 }
382
383 /* Check for any other user-defined buttons */
384 mbd = (PMSGBOXDATA)GetPropW(hwnd, L"ROS_MSGBOX");
385 if (!mbd)
386 return FALSE;
387
388 for (i = 0; i < mbd->dwButtons; ++i)
389 {
390 if (iCtrlId == mbd->pidButton[i])
391 {
392 EndDialog(hwnd, iCtrlId);
393 return TRUE;
394 }
395 }
396
397 return FALSE;
398 }
399
400 case WM_COPY:
401 MessageBoxTextToClipboard(hwnd);
402 return TRUE;
403
404 case WM_HELP:
405 {
406 LPHELPINFO phi = (LPHELPINFO)lParam;
407 mbd = (PMSGBOXDATA)GetPropW(hwnd, L"ROS_MSGBOX");
408 if (!mbd)
409 return FALSE;
410 phi->dwContextId = GetWindowContextHelpId(hwnd);
411
412 if (mbd->mbp.lpfnMsgBoxCallback)
413 {
414 mbd->mbp.lpfnMsgBoxCallback(phi);
415 }
416 else
417 {
418 HWND hwndOwner = GetWindow(hwnd, GW_OWNER);
419 if (hwndOwner)
420 SendMessageW(hwndOwner, WM_HELP, 0, lParam);
421 }
422 return TRUE;
423 }
424
425 case WM_CLOSE:
426 {
427 mbd = (PMSGBOXDATA)GetPropW(hwnd, L"ROS_MSGBOX");
428 if (!mbd)
429 return FALSE;
430
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);
436 return TRUE;
437 }
438
439 case WM_TIMER:
440 if (wParam == 0)
441 EndDialog(hwnd, IDTIMEOUT);
442 return FALSE;
443 }
444
445 return FALSE;
446 }
447
448 static int
449 MessageBoxTimeoutIndirectW(
450 CONST MSGBOXPARAMSW *lpMsgBoxParams, UINT dwTimeout)
451 {
452 int ret = 0;
453 UINT i;
454 LPWSTR defCaption = NULL;
455 MSGBOXDATA mbd;
456 MSGBTNINFO Buttons;
457 LPCWSTR ButtonText[MSGBOXEX_MAXBTNS];
458
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)
462 {
463 NTSTATUS Status;
464 UNICODE_STRING CaptionU, TextU;
465 ULONG Response = ResponseNotHandled; /* HARDERROR_RESPONSE */
466 ULONG_PTR MsgBoxParams[4] =
467 {
468 (ULONG_PTR)&TextU,
469 (ULONG_PTR)&CaptionU,
470 /*
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.
474 */
475 lpMsgBoxParams->dwStyle & ~MB_SERVICE_NOTIFICATION,
476 dwTimeout
477 };
478
479 /* hwndOwner must be NULL */
480 if (lpMsgBoxParams->hwndOwner != NULL)
481 {
482 ERR("MessageBoxTimeoutIndirectW(MB_SERVICE_NOTIFICATION): hwndOwner is not NULL!\n");
483 return 0;
484 }
485
486 //
487 // FIXME: TODO: Implement the special case for Terminal Services.
488 //
489
490 RtlInitUnicodeString(&CaptionU, lpMsgBoxParams->lpszCaption);
491 RtlInitUnicodeString(&TextU, lpMsgBoxParams->lpszText);
492
493 Status = NtRaiseHardError(STATUS_SERVICE_NOTIFICATION | HARDERROR_OVERRIDE_ERRORMODE,
494 ARRAYSIZE(MsgBoxParams),
495 (1 | 2),
496 MsgBoxParams,
497 OptionOk, /* NOTE: This parameter is ignored */
498 &Response);
499 if (!NT_SUCCESS(Status))
500 {
501 ERR("MessageBoxTimeoutIndirectW(MB_SERVICE_NOTIFICATION): NtRaiseHardError failed, Status = 0x%08lx\n", Status);
502 return 0;
503 }
504
505 /* Map the returned response to the buttons */
506 switch (Response)
507 {
508 /* Not handled */
509 case ResponseReturnToCaller:
510 case ResponseNotHandled:
511 break;
512
513 case ResponseAbort:
514 return IDABORT;
515 case ResponseCancel:
516 return IDCANCEL;
517 case ResponseIgnore:
518 return IDIGNORE;
519 case ResponseNo:
520 return IDNO;
521 case ResponseOk:
522 return IDOK;
523 case ResponseRetry:
524 return IDRETRY;
525 case ResponseYes:
526 return IDYES;
527 case ResponseTryAgain:
528 return IDTRYAGAIN;
529 case ResponseContinue:
530 return IDCONTINUE;
531
532 /* Not handled */
533 default:
534 break;
535 }
536 return 0;
537 }
538
539 ZeroMemory(&mbd, sizeof(mbd));
540 memcpy(&mbd.mbp, lpMsgBoxParams, sizeof(mbd.mbp));
541 mbd.wLanguageId = (WORD)lpMsgBoxParams->dwLanguageId;
542 mbd.dwTimeout = dwTimeout;
543
544 if (!mbd.mbp.lpszCaption)
545 {
546 /* No caption, use the default one */
547 LoadAllocStringW(User32Instance,
548 IDS_ERROR,
549 &defCaption,
550 L"Error");
551 mbd.mbp.lpszCaption = (defCaption ? defCaption : L"Error");
552 }
553
554 /* Create the selected buttons; unknown types will fall back to MB_OK */
555 i = (lpMsgBoxParams->dwStyle & MB_TYPEMASK);
556 if (i >= ARRAYSIZE(MsgBtnInfo))
557 i = MB_OK;
558
559 /* Get the buttons IDs */
560 Buttons = MsgBtnInfo[i];
561
562 /* Add the Help button */
563 if (lpMsgBoxParams->dwStyle & MB_HELP)
564 {
565 Buttons.btnIdx[Buttons.btnCnt] = IDHELP;
566 Buttons.btnIds[Buttons.btnCnt] = IDS_HELP;
567 Buttons.btnCnt++;
568 }
569
570 ASSERT(Buttons.btnCnt <= MSGBOXEX_MAXBTNS);
571
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)
575 {
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 */
579 #if 0
580 LoadAllocStringW(User32Instance,
581 Buttons.btnIds[i],
582 &ButtonText[i],
583 L"");
584 #endif
585 if (Buttons.btnIdx[i] == IDCANCEL)
586 mbd.uCancelId = IDCANCEL;
587 }
588
589 mbd.pidButton = Buttons.btnIdx;
590 mbd.ppszButtonText = ButtonText;
591 mbd.dwButtons = Buttons.btnCnt;
592
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)
596 mbd.uDefButton = 0;
597
598 /* Call the helper function */
599 ret = SoftModalMessageBox(&mbd);
600
601 #if 0
602 for (i = 0; i < mbd.dwButtons; i++)
603 {
604 if (ButtonText[i] && *ButtonText[i])
605 RtlFreeHeap(RtlGetProcessHeap(), 0, ButtonText[i]);
606 }
607 #endif
608
609 if (defCaption)
610 RtlFreeHeap(RtlGetProcessHeap(), 0, defCaption);
611
612 return ret;
613 }
614
615 int
616 WINAPI
617 SoftModalMessageBox(IN LPMSGBOXDATA lpMsgBoxData)
618 {
619 int ret = 0;
620 MSGBOXDATA mbd;
621 LPMSGBOXPARAMSW lpMsgBoxParams = &mbd.mbp;
622 DLGTEMPLATE *tpl;
623 DLGITEMTEMPLATE *iico, *itxt, *ibtn;
624 NONCLIENTMETRICSW nclm;
625 LPVOID buf;
626 BYTE *dest;
627 LPWSTR caption, text;
628 HFONT hFont, hOldFont;
629 HICON hIcon;
630 HWND hDCWnd;
631 HDC hDC;
632 SIZE units;
633 int bufsize, caplen, textlen, i, btnleft, btntop;
634 size_t ButtonLen;
635 RECT btnrect, txtrect, rc;
636 SIZE btnsize;
637 POINT iconPos; SIZE iconSize;
638
639 /* Capture the MsgBoxData */
640 memcpy(&mbd, lpMsgBoxData, sizeof(mbd));
641
642 /* Load the caption */
643 caption = NULL;
644 if (lpMsgBoxParams->lpszCaption && IS_INTRESOURCE(lpMsgBoxParams->lpszCaption))
645 {
646 /* User-defined resource string */
647 caplen = LoadAllocStringW(lpMsgBoxParams->hInstance,
648 PtrToUlong(lpMsgBoxParams->lpszCaption),
649 &caption,
650 NULL);
651 lpMsgBoxParams->lpszCaption = caption;
652 }
653 else if (lpMsgBoxParams->lpszCaption)
654 {
655 /* UNICODE string pointer */
656 caplen = wcslen(lpMsgBoxParams->lpszCaption);
657 }
658 if (!lpMsgBoxParams->lpszCaption)
659 {
660 /* No caption, use blank */
661 lpMsgBoxParams->lpszCaption = L"";
662 caplen = 0;
663 }
664
665 /* Load the text */
666 text = NULL;
667 if (lpMsgBoxParams->lpszText && IS_INTRESOURCE(lpMsgBoxParams->lpszText))
668 {
669 /* User-defined resource string */
670 textlen = LoadAllocStringW(lpMsgBoxParams->hInstance,
671 PtrToUlong(lpMsgBoxParams->lpszText),
672 &text,
673 NULL);
674 lpMsgBoxParams->lpszText = text;
675 }
676 else if (lpMsgBoxParams->lpszText)
677 {
678 /* UNICODE string pointer */
679 textlen = wcslen(lpMsgBoxParams->lpszText);
680 }
681 if (!lpMsgBoxParams->lpszText)
682 {
683 /* No text, use blank */
684 lpMsgBoxParams->lpszText = L"";
685 textlen = 0;
686 }
687
688 /* Load the icon */
689 switch (lpMsgBoxParams->dwStyle & MB_ICONMASK)
690 {
691 case MB_ICONEXCLAMATION: // case MB_ICONWARNING:
692 hIcon = LoadIconW(NULL, IDI_EXCLAMATIONW);
693 MessageBeep(MB_ICONEXCLAMATION);
694 break;
695 case MB_ICONQUESTION:
696 hIcon = LoadIconW(NULL, IDI_QUESTIONW);
697 MessageBeep(MB_ICONQUESTION);
698 break;
699 case MB_ICONASTERISK: // case MB_ICONINFORMATION:
700 hIcon = LoadIconW(NULL, IDI_ASTERISKW);
701 MessageBeep(MB_ICONASTERISK);
702 break;
703 case MB_ICONHAND: // case MB_ICONSTOP: case MB_ICONERROR:
704 hIcon = LoadIconW(NULL, IDI_HANDW);
705 MessageBeep(MB_ICONHAND);
706 break;
707 case MB_USERICON:
708 hIcon = LoadIconW(lpMsgBoxParams->hInstance, lpMsgBoxParams->lpszIcon);
709 MessageBeep(MB_OK);
710 break;
711 default:
712 /*
713 * By default, Windows 95/98/NT does not associate an icon
714 * to message boxes. So ReactOS should do the same.
715 */
716 hIcon = NULL;
717 MessageBeep(MB_OK);
718 break;
719 }
720 /* Reuse the internal pointer! */
721 lpMsgBoxParams->lpszIcon = (LPCWSTR)hIcon;
722
723 /* Basic space */
724 bufsize = sizeof(DLGTEMPLATE) +
725 2 * sizeof(WORD) + /* menu and class */
726 (caplen + 1) * sizeof(WCHAR) + /* title */
727 sizeof(WORD); /* font height */
728
729 /* Space for the icon */
730 if (hIcon)
731 {
732 bufsize = ALIGN_UP(bufsize, DWORD);
733 bufsize += sizeof(DLGITEMTEMPLATE) +
734 4 * sizeof(WORD) +
735 sizeof(WCHAR);
736 }
737
738 /* Space for the text */
739 bufsize = ALIGN_UP(bufsize, DWORD);
740 bufsize += sizeof(DLGITEMTEMPLATE) +
741 3 * sizeof(WORD) +
742 (textlen + 1) * sizeof(WCHAR);
743
744 /* Space for the buttons */
745 for (i = 0; i < mbd.dwButtons; i++)
746 {
747 if (!mbd.ppszButtonText[i] || !*mbd.ppszButtonText[i])
748 {
749 /* No text, use blank */
750 mbd.ppszButtonText[i] = L"";
751 ButtonLen = 0;
752 }
753 else
754 {
755 /* UNICODE string pointer */
756 ButtonLen = wcslen(mbd.ppszButtonText[i]);
757 }
758
759 bufsize = ALIGN_UP(bufsize, DWORD);
760 bufsize += sizeof(DLGITEMTEMPLATE) +
761 3 * sizeof(WORD) +
762 (ButtonLen + 1) * sizeof(WCHAR);
763 }
764
765 /* Allocate the dialog template */
766 buf = RtlAllocateHeap(RtlGetProcessHeap(), 0, bufsize);
767 if (!buf)
768 goto Quit;
769
770 iico = itxt = NULL;
771
772
773 nclm.cbSize = sizeof(nclm);
774 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(nclm), &nclm, 0);
775 hFont = CreateFontIndirectW(&nclm.lfMessageFont);
776 if (!hFont)
777 {
778 ERR("Cannot retrieve nclm.lfMessageFont! (error %lu)\n", GetLastError());
779 goto Quit;
780 }
781
782 hDCWnd = NULL;
783 hDC = GetDCEx(hDCWnd, NULL, DCX_WINDOW | DCX_CACHE);
784 if (!hDC)
785 {
786 /* Retry with the DC of the owner window */
787 hDCWnd = lpMsgBoxParams->hwndOwner;
788 hDC = GetDCEx(hDCWnd, NULL, DCX_WINDOW | DCX_CACHE);
789 }
790 if (!hDC)
791 {
792 ERR("GetDCEx() failed, bail out! (error %lu)\n", GetLastError());
793 goto Quit;
794 }
795 hOldFont = SelectObject(hDC, hFont);
796
797 units.cx = GdiGetCharDimensions(hDC, NULL, &units.cy);
798 if (!units.cx)
799 {
800 DWORD defUnits;
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);
805 }
806
807 /* Calculate the caption rectangle */
808 txtrect.right = MulDiv(GetSystemMetrics(SM_CXSCREEN), 4, 5);
809 if (hIcon)
810 txtrect.right -= GetSystemMetrics(SM_CXICON) + MSGBOXEX_SPACING;
811 txtrect.top = txtrect.left = txtrect.bottom = 0;
812 if (textlen != 0)
813 {
814 DrawTextW(hDC, lpMsgBoxParams->lpszText, textlen, &txtrect,
815 DT_LEFT | DT_NOPREFIX | DT_WORDBREAK | DT_EXPANDTABS | DT_EXTERNALLEADING | DT_EDITCONTROL | DT_CALCRECT);
816 }
817 else
818 {
819 txtrect.right = txtrect.left + 1;
820 txtrect.bottom = txtrect.top + 1;
821 }
822 txtrect.right++;
823
824 /* Calculate the maximum buttons size */
825 btnsize.cx = BTN_CX;
826 btnsize.cy = BTN_CY;
827 btnrect.left = btnrect.top = 0;
828 for (i = 0; i < mbd.dwButtons; i++)
829 {
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);
835 }
836
837 if (hOldFont)
838 SelectObject(hDC, hOldFont);
839
840 ReleaseDC(hDCWnd, hDC);
841
842 if (hFont)
843 DeleteObject(hFont);
844
845
846 /* Calculate position and size of controls */
847
848
849 /* Calculate position and size of the icon */
850 rc.left = rc.bottom = rc.right = 0;
851 btntop = 0;
852 if (hIcon)
853 {
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);
859 #else
860 rc.top = MSGBOXEX_MARGIN;
861 #endif
862 btnleft = (mbd.dwButtons * (btnsize.cx + MSGBOXEX_BUTTONSPACING)) - MSGBOXEX_BUTTONSPACING;
863 if (btnleft > txtrect.right + rc.right + MSGBOXEX_SPACING)
864 {
865 #ifdef MSGBOX_TEXTHCENTER
866 rc.left = MSGBOXEX_MARGIN + ((btnleft - txtrect.right - rc.right - MSGBOXEX_SPACING) / 2);
867 #else
868 rc.left = MSGBOXEX_MARGIN;
869 #endif
870 btnleft = MSGBOXEX_MARGIN;
871 }
872 else
873 {
874 rc.left = MSGBOXEX_MARGIN;
875 btnleft = MSGBOXEX_MARGIN + ((txtrect.right + rc.right + MSGBOXEX_SPACING - btnleft) / 2);
876 }
877
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);
882
883 btntop = rc.top + rc.bottom + MSGBOXEX_SPACING;
884 rc.left += rc.right + MSGBOXEX_SPACING;
885 }
886 else
887 {
888 btnleft = (mbd.dwButtons * (btnsize.cx + MSGBOXEX_BUTTONSPACING)) - MSGBOXEX_BUTTONSPACING;
889 if (btnleft > txtrect.right)
890 {
891 #ifdef MSGBOX_TEXTHCENTER
892 rc.left = MSGBOXEX_MARGIN + ((btnleft - txtrect.right) / 2);
893 #else
894 rc.left = MSGBOXEX_MARGIN;
895 #endif
896 btnleft = MSGBOXEX_MARGIN;
897 }
898 else
899 {
900 rc.left = MSGBOXEX_MARGIN;
901 btnleft = MSGBOXEX_MARGIN + ((txtrect.right - btnleft) / 2);
902 }
903 }
904
905
906 /* Initialize the dialog template */
907 tpl = (DLGTEMPLATE *)buf;
908
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;
916 tpl->x = 100;
917 tpl->y = 100;
918 tpl->cdit = mbd.dwButtons + (hIcon ? 1 : 0) + 1; /* Buttons, icon and text */
919
920 dest = (BYTE *)(tpl + 1);
921
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);
928
929 /*
930 * A font point size (height) of 0x7FFF means that we use
931 * the message box font (NONCLIENTMETRICSW.lfMessageFont).
932 */
933 *(WORD*)dest = 0x7FFF;
934 dest += sizeof(WORD);
935
936 /* Create the icon */
937 if (hIcon)
938 {
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;
944
945 iico->x = iconPos.x;
946 iico->y = iconPos.y;
947 iico->cx = iconSize.cx;
948 iico->cy = iconSize.cy;
949
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);
957 *(WCHAR*)dest = 0;
958 dest += sizeof(WCHAR);
959 *(WORD*)dest = 0;
960 dest += sizeof(WORD);
961 }
962
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;
969 else
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);
980 *(WCHAR*)dest = 0;
981 dest += sizeof(WCHAR);
982 *(WORD*)dest = 0;
983 dest += sizeof(WORD);
984
985
986 /* Calculate position of the text */
987 rc.top = MSGBOXEX_MARGIN + ((rc.bottom - txtrect.bottom) / 2);
988 rc.top = max(rc.top, MSGBOXEX_MARGIN);
989
990
991 /* Make the first button the default button if none other is */
992 if (mbd.uDefButton >= mbd.dwButtons)
993 mbd.uDefButton = 0;
994
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++)
998 {
999 ButtonLen = wcslen(mbd.ppszButtonText[i]);
1000
1001 dest = ALIGN_UP_POINTER(dest, DWORD);
1002 ibtn = (DLGITEMTEMPLATE *)dest;
1003
1004 ibtn->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
1005 if (i == mbd.uDefButton)
1006 ibtn->style |= BS_DEFPUSHBUTTON;
1007 else
1008 ibtn->style |= BS_PUSHBUTTON;
1009
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);
1019 *(WORD*)dest = 0;
1020 dest += sizeof(WORD);
1021 *(WORD*)dest = 0;
1022 dest += sizeof(WORD);
1023
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;
1029 }
1030
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;
1036
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);
1042
1043 /* Set the size of the window */
1044 tpl->cx = RESCALE_X(btnleft, units);
1045 tpl->cy = RESCALE_Y(btntop, units);
1046
1047 /* Finally show the message-box */
1048 ret = DialogBoxIndirectParamW(lpMsgBoxParams->hInstance, tpl,
1049 lpMsgBoxParams->hwndOwner,
1050 MessageBoxProc, (LPARAM)&mbd);
1051
1052 Quit:
1053 if (buf)
1054 RtlFreeHeap(RtlGetProcessHeap(), 0, buf);
1055
1056 if (text)
1057 RtlFreeHeap(RtlGetProcessHeap(), 0, text);
1058
1059 if (caption)
1060 RtlFreeHeap(RtlGetProcessHeap(), 0, caption);
1061
1062 return ret;
1063 }
1064
1065
1066 /* FUNCTIONS *****************************************************************/
1067
1068 /*
1069 * @implemented
1070 */
1071 int
1072 WINAPI
1073 MessageBoxA(
1074 IN HWND hWnd,
1075 IN LPCSTR lpText,
1076 IN LPCSTR lpCaption,
1077 IN UINT uType)
1078 {
1079 return MessageBoxExA(hWnd, lpText, lpCaption, uType, LANG_NEUTRAL);
1080 }
1081
1082 /*
1083 * @implemented
1084 */
1085 int
1086 WINAPI
1087 MessageBoxW(
1088 IN HWND hWnd,
1089 IN LPCWSTR lpText,
1090 IN LPCWSTR lpCaption,
1091 IN UINT uType)
1092 {
1093 return MessageBoxExW(hWnd, lpText, lpCaption, uType, LANG_NEUTRAL);
1094 }
1095
1096
1097 /*
1098 * @implemented
1099 */
1100 int
1101 WINAPI
1102 MessageBoxExA(
1103 IN HWND hWnd,
1104 IN LPCSTR lpText,
1105 IN LPCSTR lpCaption,
1106 IN UINT uType,
1107 IN WORD wLanguageId)
1108 {
1109 MSGBOXPARAMSA msgbox;
1110
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;
1121
1122 return MessageBoxIndirectA(&msgbox);
1123 }
1124
1125 /*
1126 * @implemented
1127 */
1128 int
1129 WINAPI
1130 MessageBoxExW(
1131 IN HWND hWnd,
1132 IN LPCWSTR lpText,
1133 IN LPCWSTR lpCaption,
1134 IN UINT uType,
1135 IN WORD wLanguageId)
1136 {
1137 MSGBOXPARAMSW msgbox;
1138
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;
1149
1150 return MessageBoxTimeoutIndirectW(&msgbox, (UINT)-1);
1151 }
1152
1153
1154 /*
1155 * @implemented
1156 */
1157 int
1158 WINAPI
1159 MessageBoxIndirectA(
1160 IN CONST MSGBOXPARAMSA* lpMsgBoxParams)
1161 {
1162 MSGBOXPARAMSW msgboxW;
1163 UNICODE_STRING textW, captionW, iconW;
1164 int ret;
1165
1166 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszText))
1167 {
1168 RtlCreateUnicodeStringFromAsciiz(&textW, (PCSZ)lpMsgBoxParams->lpszText);
1169 /*
1170 * UNICODE_STRING objects are always allocated with an extra byte so you
1171 * can null-term if you want
1172 */
1173 textW.Buffer[textW.Length / sizeof(WCHAR)] = L'\0';
1174 }
1175 else
1176 textW.Buffer = (LPWSTR)lpMsgBoxParams->lpszText;
1177
1178 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszCaption))
1179 {
1180 RtlCreateUnicodeStringFromAsciiz(&captionW, (PCSZ)lpMsgBoxParams->lpszCaption);
1181 /*
1182 * UNICODE_STRING objects are always allocated with an extra byte so you
1183 * can null-term if you want
1184 */
1185 captionW.Buffer[captionW.Length / sizeof(WCHAR)] = L'\0';
1186 }
1187 else
1188 captionW.Buffer = (LPWSTR)lpMsgBoxParams->lpszCaption;
1189
1190 if (lpMsgBoxParams->dwStyle & MB_USERICON)
1191 {
1192 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszIcon))
1193 {
1194 RtlCreateUnicodeStringFromAsciiz(&iconW, (PCSZ)lpMsgBoxParams->lpszIcon);
1195 /*
1196 * UNICODE_STRING objects are always allocated with an extra byte so you
1197 * can null-term if you want
1198 */
1199 iconW.Buffer[iconW.Length / sizeof(WCHAR)] = L'\0';
1200 }
1201 else
1202 iconW.Buffer = (LPWSTR)lpMsgBoxParams->lpszIcon;
1203 }
1204 else
1205 iconW.Buffer = NULL;
1206
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;
1217
1218 ret = MessageBoxTimeoutIndirectW(&msgboxW, (UINT)-1);
1219
1220 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszText))
1221 RtlFreeUnicodeString(&textW);
1222
1223 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszCaption))
1224 RtlFreeUnicodeString(&captionW);
1225
1226 if ((lpMsgBoxParams->dwStyle & MB_USERICON) && !IS_INTRESOURCE(iconW.Buffer))
1227 RtlFreeUnicodeString(&iconW);
1228
1229 return ret;
1230 }
1231
1232 /*
1233 * @implemented
1234 */
1235 int
1236 WINAPI
1237 MessageBoxIndirectW(
1238 IN CONST MSGBOXPARAMSW* lpMsgBoxParams)
1239 {
1240 return MessageBoxTimeoutIndirectW(lpMsgBoxParams, (UINT)-1);
1241 }
1242
1243
1244 /*
1245 * @implemented
1246 */
1247 int
1248 WINAPI
1249 MessageBoxTimeoutA(
1250 IN HWND hWnd,
1251 IN LPCSTR lpText,
1252 IN LPCSTR lpCaption,
1253 IN UINT uType,
1254 IN WORD wLanguageId,
1255 IN DWORD dwTimeout)
1256 {
1257 MSGBOXPARAMSW msgboxW;
1258 UNICODE_STRING textW, captionW;
1259 int ret;
1260
1261 if (!IS_INTRESOURCE(lpText))
1262 RtlCreateUnicodeStringFromAsciiz(&textW, (PCSZ)lpText);
1263 else
1264 textW.Buffer = (LPWSTR)lpText;
1265
1266 if (!IS_INTRESOURCE(lpCaption))
1267 RtlCreateUnicodeStringFromAsciiz(&captionW, (PCSZ)lpCaption);
1268 else
1269 captionW.Buffer = (LPWSTR)lpCaption;
1270
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;
1281
1282 ret = MessageBoxTimeoutIndirectW(&msgboxW, (UINT)dwTimeout);
1283
1284 if (!IS_INTRESOURCE(textW.Buffer))
1285 RtlFreeUnicodeString(&textW);
1286
1287 if (!IS_INTRESOURCE(captionW.Buffer))
1288 RtlFreeUnicodeString(&captionW);
1289
1290 return ret;
1291 }
1292
1293 /*
1294 * @implemented
1295 */
1296 int
1297 WINAPI
1298 MessageBoxTimeoutW(
1299 IN HWND hWnd,
1300 IN LPCWSTR lpText,
1301 IN LPCWSTR lpCaption,
1302 IN UINT uType,
1303 IN WORD wLanguageId,
1304 IN DWORD dwTimeout)
1305 {
1306 MSGBOXPARAMSW msgbox;
1307
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;
1318
1319 return MessageBoxTimeoutIndirectW(&msgbox, (UINT)dwTimeout);
1320 }
1321
1322
1323 /*
1324 * @implemented
1325 */
1326 BOOL
1327 WINAPI
1328 MessageBeep(
1329 IN UINT uType)
1330 {
1331 return NtUserxMessageBeep(uType);
1332 }
1333
1334
1335 /*
1336 * @implemented
1337 *
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.
1341 */
1342 LPCWSTR
1343 WINAPI
1344 MB_GetString(
1345 IN UINT wBtn)
1346 {
1347 static BOOL bCached = FALSE;
1348 static MBSTRING MBStrings[MAX_MB_STRINGS]; // FIXME: Use gpsi->MBStrings when this is loaded by Win32k!
1349
1350 //
1351 // FIXME - TODO: The gpsi->MBStrings[] array should be loaded by win32k!
1352 //
1353 ASSERT(IDCONTINUE <= MAX_MB_STRINGS);
1354 if (!bCached)
1355 {
1356 UINT i;
1357 for (i = 0; i < MAX_MB_STRINGS; ++i)
1358 {
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));
1365 }
1366 bCached = TRUE;
1367 }
1368
1369 /*
1370 * The allowable IDs are between "IDOK - 1" (0) and "IDCONTINUE - 1" (10) inclusive.
1371 * See psdk/winuser.h and user32/include/resource.h .
1372 */
1373 if (wBtn > IDCONTINUE - 1)
1374 return NULL;
1375
1376 return /*gpsi->*/MBStrings[wBtn].szName;
1377 }
1378
1379 /* EOF */