[USER32] Log the text of message boxes to help figure out some test bot timeouts.
[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 ERR("MessageBox: %s\n", wine_dbgstr_wn(lpMsgBoxParams->lpszText, textlen));
1049 ret = DialogBoxIndirectParamW(lpMsgBoxParams->hInstance, tpl,
1050 lpMsgBoxParams->hwndOwner,
1051 MessageBoxProc, (LPARAM)&mbd);
1052
1053 Quit:
1054 if (buf)
1055 RtlFreeHeap(RtlGetProcessHeap(), 0, buf);
1056
1057 if (text)
1058 RtlFreeHeap(RtlGetProcessHeap(), 0, text);
1059
1060 if (caption)
1061 RtlFreeHeap(RtlGetProcessHeap(), 0, caption);
1062
1063 return ret;
1064 }
1065
1066
1067 /* FUNCTIONS *****************************************************************/
1068
1069 /*
1070 * @implemented
1071 */
1072 int
1073 WINAPI
1074 MessageBoxA(
1075 IN HWND hWnd,
1076 IN LPCSTR lpText,
1077 IN LPCSTR lpCaption,
1078 IN UINT uType)
1079 {
1080 return MessageBoxExA(hWnd, lpText, lpCaption, uType, LANG_NEUTRAL);
1081 }
1082
1083 /*
1084 * @implemented
1085 */
1086 int
1087 WINAPI
1088 MessageBoxW(
1089 IN HWND hWnd,
1090 IN LPCWSTR lpText,
1091 IN LPCWSTR lpCaption,
1092 IN UINT uType)
1093 {
1094 return MessageBoxExW(hWnd, lpText, lpCaption, uType, LANG_NEUTRAL);
1095 }
1096
1097
1098 /*
1099 * @implemented
1100 */
1101 int
1102 WINAPI
1103 MessageBoxExA(
1104 IN HWND hWnd,
1105 IN LPCSTR lpText,
1106 IN LPCSTR lpCaption,
1107 IN UINT uType,
1108 IN WORD wLanguageId)
1109 {
1110 MSGBOXPARAMSA msgbox;
1111
1112 msgbox.cbSize = sizeof(msgbox);
1113 msgbox.hwndOwner = hWnd;
1114 msgbox.hInstance = 0;
1115 msgbox.lpszText = lpText;
1116 msgbox.lpszCaption = lpCaption;
1117 msgbox.dwStyle = uType;
1118 msgbox.lpszIcon = NULL;
1119 msgbox.dwContextHelpId = 0;
1120 msgbox.lpfnMsgBoxCallback = NULL;
1121 msgbox.dwLanguageId = wLanguageId;
1122
1123 return MessageBoxIndirectA(&msgbox);
1124 }
1125
1126 /*
1127 * @implemented
1128 */
1129 int
1130 WINAPI
1131 MessageBoxExW(
1132 IN HWND hWnd,
1133 IN LPCWSTR lpText,
1134 IN LPCWSTR lpCaption,
1135 IN UINT uType,
1136 IN WORD wLanguageId)
1137 {
1138 MSGBOXPARAMSW msgbox;
1139
1140 msgbox.cbSize = sizeof(msgbox);
1141 msgbox.hwndOwner = hWnd;
1142 msgbox.hInstance = 0;
1143 msgbox.lpszText = lpText;
1144 msgbox.lpszCaption = lpCaption;
1145 msgbox.dwStyle = uType;
1146 msgbox.lpszIcon = NULL;
1147 msgbox.dwContextHelpId = 0;
1148 msgbox.lpfnMsgBoxCallback = NULL;
1149 msgbox.dwLanguageId = wLanguageId;
1150
1151 return MessageBoxTimeoutIndirectW(&msgbox, (UINT)-1);
1152 }
1153
1154
1155 /*
1156 * @implemented
1157 */
1158 int
1159 WINAPI
1160 MessageBoxIndirectA(
1161 IN CONST MSGBOXPARAMSA* lpMsgBoxParams)
1162 {
1163 MSGBOXPARAMSW msgboxW;
1164 UNICODE_STRING textW, captionW, iconW;
1165 int ret;
1166
1167 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszText))
1168 {
1169 RtlCreateUnicodeStringFromAsciiz(&textW, (PCSZ)lpMsgBoxParams->lpszText);
1170 /*
1171 * UNICODE_STRING objects are always allocated with an extra byte so you
1172 * can null-term if you want
1173 */
1174 textW.Buffer[textW.Length / sizeof(WCHAR)] = L'\0';
1175 }
1176 else
1177 textW.Buffer = (LPWSTR)lpMsgBoxParams->lpszText;
1178
1179 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszCaption))
1180 {
1181 RtlCreateUnicodeStringFromAsciiz(&captionW, (PCSZ)lpMsgBoxParams->lpszCaption);
1182 /*
1183 * UNICODE_STRING objects are always allocated with an extra byte so you
1184 * can null-term if you want
1185 */
1186 captionW.Buffer[captionW.Length / sizeof(WCHAR)] = L'\0';
1187 }
1188 else
1189 captionW.Buffer = (LPWSTR)lpMsgBoxParams->lpszCaption;
1190
1191 if (lpMsgBoxParams->dwStyle & MB_USERICON)
1192 {
1193 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszIcon))
1194 {
1195 RtlCreateUnicodeStringFromAsciiz(&iconW, (PCSZ)lpMsgBoxParams->lpszIcon);
1196 /*
1197 * UNICODE_STRING objects are always allocated with an extra byte so you
1198 * can null-term if you want
1199 */
1200 iconW.Buffer[iconW.Length / sizeof(WCHAR)] = L'\0';
1201 }
1202 else
1203 iconW.Buffer = (LPWSTR)lpMsgBoxParams->lpszIcon;
1204 }
1205 else
1206 iconW.Buffer = NULL;
1207
1208 msgboxW.cbSize = sizeof(msgboxW);
1209 msgboxW.hwndOwner = lpMsgBoxParams->hwndOwner;
1210 msgboxW.hInstance = lpMsgBoxParams->hInstance;
1211 msgboxW.lpszText = textW.Buffer;
1212 msgboxW.lpszCaption = captionW.Buffer;
1213 msgboxW.dwStyle = lpMsgBoxParams->dwStyle;
1214 msgboxW.lpszIcon = iconW.Buffer;
1215 msgboxW.dwContextHelpId = lpMsgBoxParams->dwContextHelpId;
1216 msgboxW.lpfnMsgBoxCallback = lpMsgBoxParams->lpfnMsgBoxCallback;
1217 msgboxW.dwLanguageId = lpMsgBoxParams->dwLanguageId;
1218
1219 ret = MessageBoxTimeoutIndirectW(&msgboxW, (UINT)-1);
1220
1221 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszText))
1222 RtlFreeUnicodeString(&textW);
1223
1224 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszCaption))
1225 RtlFreeUnicodeString(&captionW);
1226
1227 if ((lpMsgBoxParams->dwStyle & MB_USERICON) && !IS_INTRESOURCE(iconW.Buffer))
1228 RtlFreeUnicodeString(&iconW);
1229
1230 return ret;
1231 }
1232
1233 /*
1234 * @implemented
1235 */
1236 int
1237 WINAPI
1238 MessageBoxIndirectW(
1239 IN CONST MSGBOXPARAMSW* lpMsgBoxParams)
1240 {
1241 return MessageBoxTimeoutIndirectW(lpMsgBoxParams, (UINT)-1);
1242 }
1243
1244
1245 /*
1246 * @implemented
1247 */
1248 int
1249 WINAPI
1250 MessageBoxTimeoutA(
1251 IN HWND hWnd,
1252 IN LPCSTR lpText,
1253 IN LPCSTR lpCaption,
1254 IN UINT uType,
1255 IN WORD wLanguageId,
1256 IN DWORD dwTimeout)
1257 {
1258 MSGBOXPARAMSW msgboxW;
1259 UNICODE_STRING textW, captionW;
1260 int ret;
1261
1262 if (!IS_INTRESOURCE(lpText))
1263 RtlCreateUnicodeStringFromAsciiz(&textW, (PCSZ)lpText);
1264 else
1265 textW.Buffer = (LPWSTR)lpText;
1266
1267 if (!IS_INTRESOURCE(lpCaption))
1268 RtlCreateUnicodeStringFromAsciiz(&captionW, (PCSZ)lpCaption);
1269 else
1270 captionW.Buffer = (LPWSTR)lpCaption;
1271
1272 msgboxW.cbSize = sizeof(msgboxW);
1273 msgboxW.hwndOwner = hWnd;
1274 msgboxW.hInstance = 0;
1275 msgboxW.lpszText = textW.Buffer;
1276 msgboxW.lpszCaption = captionW.Buffer;
1277 msgboxW.dwStyle = uType;
1278 msgboxW.lpszIcon = NULL;
1279 msgboxW.dwContextHelpId = 0;
1280 msgboxW.lpfnMsgBoxCallback = NULL;
1281 msgboxW.dwLanguageId = wLanguageId;
1282
1283 ret = MessageBoxTimeoutIndirectW(&msgboxW, (UINT)dwTimeout);
1284
1285 if (!IS_INTRESOURCE(textW.Buffer))
1286 RtlFreeUnicodeString(&textW);
1287
1288 if (!IS_INTRESOURCE(captionW.Buffer))
1289 RtlFreeUnicodeString(&captionW);
1290
1291 return ret;
1292 }
1293
1294 /*
1295 * @implemented
1296 */
1297 int
1298 WINAPI
1299 MessageBoxTimeoutW(
1300 IN HWND hWnd,
1301 IN LPCWSTR lpText,
1302 IN LPCWSTR lpCaption,
1303 IN UINT uType,
1304 IN WORD wLanguageId,
1305 IN DWORD dwTimeout)
1306 {
1307 MSGBOXPARAMSW msgbox;
1308
1309 msgbox.cbSize = sizeof(msgbox);
1310 msgbox.hwndOwner = hWnd;
1311 msgbox.hInstance = 0;
1312 msgbox.lpszText = lpText;
1313 msgbox.lpszCaption = lpCaption;
1314 msgbox.dwStyle = uType;
1315 msgbox.lpszIcon = NULL;
1316 msgbox.dwContextHelpId = 0;
1317 msgbox.lpfnMsgBoxCallback = NULL;
1318 msgbox.dwLanguageId = wLanguageId;
1319
1320 return MessageBoxTimeoutIndirectW(&msgbox, (UINT)dwTimeout);
1321 }
1322
1323
1324 /*
1325 * @implemented
1326 */
1327 BOOL
1328 WINAPI
1329 MessageBeep(
1330 IN UINT uType)
1331 {
1332 return NtUserxMessageBeep(uType);
1333 }
1334
1335
1336 /*
1337 * @implemented
1338 *
1339 * See: https://msdn.microsoft.com/en-us/library/windows/desktop/dn910915(v=vs.85).aspx
1340 * and: http://undoc.airesoft.co.uk/user32.dll/MB_GetString.php
1341 * for more information.
1342 */
1343 LPCWSTR
1344 WINAPI
1345 MB_GetString(
1346 IN UINT wBtn)
1347 {
1348 static BOOL bCached = FALSE;
1349 static MBSTRING MBStrings[MAX_MB_STRINGS]; // FIXME: Use gpsi->MBStrings when this is loaded by Win32k!
1350
1351 //
1352 // FIXME - TODO: The gpsi->MBStrings[] array should be loaded by win32k!
1353 //
1354 ASSERT(IDCONTINUE <= MAX_MB_STRINGS);
1355 if (!bCached)
1356 {
1357 UINT i;
1358 for (i = 0; i < MAX_MB_STRINGS; ++i)
1359 {
1360 /*gpsi->*/MBStrings[i].uID = IDOK + i;
1361 /*gpsi->*/MBStrings[i].uStr = IDS_OK + i; // See user32/include/resource.h
1362 LoadStringW(User32Instance,
1363 /*gpsi->*/MBStrings[i].uStr,
1364 /*gpsi->*/MBStrings[i].szName,
1365 ARRAYSIZE(/*gpsi->*/MBStrings[i].szName));
1366 }
1367 bCached = TRUE;
1368 }
1369
1370 /*
1371 * The allowable IDs are between "IDOK - 1" (0) and "IDCONTINUE - 1" (10) inclusive.
1372 * See psdk/winuser.h and user32/include/resource.h .
1373 */
1374 if (wBtn > IDCONTINUE - 1)
1375 return NULL;
1376
1377 return /*gpsi->*/MBStrings[wBtn].szName;
1378 }
1379
1380 /* EOF */