xref: /reactos/dll/win32/msgina/shutdown.c (revision cc439606)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS msgina.dll
4  * FILE:            lib/msgina/shutdown.c
5  * PURPOSE:         Shutdown Dialog Box (GUI only)
6  * PROGRAMMERS:     Lee Schroeder (spaceseel at gmail dot com)
7  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8  */
9 
10 #include "msgina.h"
11 #include <powrprof.h>
12 #include <wingdi.h>
13 
14 /* Shutdown state flags */
15 #define WLX_SHUTDOWN_STATE_LOGOFF       0x01
16 #define WLX_SHUTDOWN_STATE_POWER_OFF    0x02
17 #define WLX_SHUTDOWN_STATE_REBOOT       0x04
18 // 0x08
19 #define WLX_SHUTDOWN_STATE_SLEEP        0x10
20 // 0x20
21 #define WLX_SHUTDOWN_STATE_HIBERNATE    0x40
22 // 0x80
23 
24 typedef struct _SHUTDOWN_DLG_CONTEXT
25 {
26     PGINA_CONTEXT pgContext;
27     HBITMAP hBitmap;
28     DWORD ShutdownOptions;
29     BOOL bCloseDlg;
30     BOOL bReasonUI;
31 } SHUTDOWN_DLG_CONTEXT, *PSHUTDOWN_DLG_CONTEXT;
32 
33 
34 static
35 BOOL
36 GetShutdownReasonUI(VOID)
37 {
38     OSVERSIONINFOEX VersionInfo;
39     DWORD dwValue, dwSize;
40     HKEY hKey;
41     LONG lRet;
42 
43     /* Query the policy value */
44     lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
45                          L"Software\\Policies\\Microsoft\\Windows NT\\Reliability",
46                          0,
47                          KEY_QUERY_VALUE,
48                          &hKey);
49     if (lRet == ERROR_SUCCESS)
50     {
51         dwValue = 0;
52         dwSize = sizeof(dwValue);
53         RegQueryValueExW(hKey,
54                          L"ShutdownReasonUI",
55                          NULL,
56                          NULL,
57                          (LPBYTE)&dwValue,
58                          &dwSize);
59         RegCloseKey(hKey);
60 
61         return (dwValue != 0) ? TRUE : FALSE;
62     }
63 
64     /* Query the machine value */
65     lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
66                          L"Software\\Microsoft\\Windows\\CurrentVersion\\Reliability",
67                          0,
68                          KEY_QUERY_VALUE,
69                          &hKey);
70     if (lRet == ERROR_SUCCESS)
71     {
72         dwValue = 0;
73         dwSize = sizeof(dwValue);
74         RegQueryValueExW(hKey,
75                          L"ShutdownReasonUI",
76                          NULL,
77                          NULL,
78                          (LPBYTE)&dwValue,
79                          &dwSize);
80         RegCloseKey(hKey);
81 
82         return (dwValue != 0) ? TRUE : FALSE;
83     }
84 
85     /* Return the default value */
86     VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo);
87     if (!GetVersionEx((POSVERSIONINFO)&VersionInfo))
88         return FALSE;
89 
90     return FALSE;
91 //    return (VersionInfo.wProductType == VER_NT_WORKSTATION) ? FALSE : TRUE;
92 }
93 
94 DWORD
95 GetDefaultShutdownSelState(VOID)
96 {
97     return WLX_SAS_ACTION_SHUTDOWN_POWER_OFF;
98 }
99 
100 DWORD
101 LoadShutdownSelState(VOID)
102 {
103     LONG lRet;
104     HKEY hKeyCurrentUser, hKey;
105     DWORD dwValue, dwTemp, dwSize;
106 
107     /* Default to shutdown */
108     dwValue = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF;
109 
110     /* Open the current user HKCU key */
111     lRet = RegOpenCurrentUser(MAXIMUM_ALLOWED, &hKeyCurrentUser);
112     if (lRet == ERROR_SUCCESS)
113     {
114         /* Open the subkey */
115         lRet = RegOpenKeyExW(hKeyCurrentUser,
116                              L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
117                              0, KEY_QUERY_VALUE, &hKey);
118         RegCloseKey(hKeyCurrentUser);
119     }
120     if (lRet != ERROR_SUCCESS)
121         return dwValue;
122 
123     /* Read the value */
124     dwSize = sizeof(dwTemp);
125     lRet = RegQueryValueExW(hKey,
126                             L"Shutdown Setting",
127                             NULL, NULL,
128                             (LPBYTE)&dwTemp, &dwSize);
129     RegCloseKey(hKey);
130 
131     if (lRet == ERROR_SUCCESS)
132     {
133         switch (dwTemp)
134         {
135             case WLX_SHUTDOWN_STATE_LOGOFF:
136                 dwValue = WLX_SAS_ACTION_LOGOFF;
137                 break;
138 
139             case WLX_SHUTDOWN_STATE_POWER_OFF:
140                 dwValue = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF;
141                 break;
142 
143             case WLX_SHUTDOWN_STATE_REBOOT:
144                 dwValue = WLX_SAS_ACTION_SHUTDOWN_REBOOT;
145                 break;
146 
147             // 0x08
148 
149             case WLX_SHUTDOWN_STATE_SLEEP:
150                 dwValue = WLX_SAS_ACTION_SHUTDOWN_SLEEP;
151                 break;
152 
153             // 0x20
154 
155             case WLX_SHUTDOWN_STATE_HIBERNATE:
156                 dwValue = WLX_SAS_ACTION_SHUTDOWN_HIBERNATE;
157                 break;
158 
159             // 0x80
160         }
161     }
162 
163     return dwValue;
164 }
165 
166 VOID
167 SaveShutdownSelState(
168     IN DWORD ShutdownCode)
169 {
170     LONG lRet;
171     HKEY hKeyCurrentUser, hKey;
172     DWORD dwValue = 0;
173 
174     /* Open the current user HKCU key */
175     lRet = RegOpenCurrentUser(MAXIMUM_ALLOWED, &hKeyCurrentUser);
176     if (lRet == ERROR_SUCCESS)
177     {
178         /* Create the subkey */
179         lRet = RegCreateKeyExW(hKeyCurrentUser,
180                                L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
181                                0, NULL,
182                                REG_OPTION_NON_VOLATILE,
183                                KEY_SET_VALUE,
184                                NULL, &hKey, NULL);
185         RegCloseKey(hKeyCurrentUser);
186     }
187     if (lRet != ERROR_SUCCESS)
188         return;
189 
190     switch (ShutdownCode)
191     {
192         case WLX_SAS_ACTION_LOGOFF:
193             dwValue = WLX_SHUTDOWN_STATE_LOGOFF;
194             break;
195 
196         case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF:
197             dwValue = WLX_SHUTDOWN_STATE_POWER_OFF;
198             break;
199 
200         case WLX_SAS_ACTION_SHUTDOWN_REBOOT:
201             dwValue = WLX_SHUTDOWN_STATE_REBOOT;
202             break;
203 
204         case WLX_SAS_ACTION_SHUTDOWN_SLEEP:
205             dwValue = WLX_SHUTDOWN_STATE_SLEEP;
206             break;
207 
208         case WLX_SAS_ACTION_SHUTDOWN_HIBERNATE:
209             dwValue = WLX_SHUTDOWN_STATE_HIBERNATE;
210             break;
211     }
212 
213     RegSetValueExW(hKey,
214                    L"Shutdown Setting",
215                    0, REG_DWORD,
216                    (LPBYTE)&dwValue, sizeof(dwValue));
217     RegCloseKey(hKey);
218 }
219 
220 DWORD
221 GetDefaultShutdownOptions(VOID)
222 {
223     return WLX_SHUTDOWN_STATE_POWER_OFF | WLX_SHUTDOWN_STATE_REBOOT;
224 }
225 
226 DWORD
227 GetAllowedShutdownOptions(VOID)
228 {
229     DWORD Options = 0;
230 
231     // FIXME: Compute those options accordings to current user's rights!
232     Options |= WLX_SHUTDOWN_STATE_LOGOFF | WLX_SHUTDOWN_STATE_POWER_OFF | WLX_SHUTDOWN_STATE_REBOOT;
233 
234     if (IsPwrSuspendAllowed())
235         Options |= WLX_SHUTDOWN_STATE_SLEEP;
236 
237     if (IsPwrHibernateAllowed())
238         Options |= WLX_SHUTDOWN_STATE_HIBERNATE;
239 
240     return Options;
241 }
242 
243 static VOID
244 UpdateShutdownDesc(
245     IN HWND hDlg,
246     IN PSHUTDOWN_DLG_CONTEXT pContext) // HINSTANCE hInstance
247 {
248     UINT DescId = 0;
249     DWORD ShutdownCode;
250     WCHAR szBuffer[256];
251 
252     ShutdownCode = SendDlgItemMessageW(hDlg, IDC_SHUTDOWN_ACTION, CB_GETCURSEL, 0, 0);
253     if (ShutdownCode == CB_ERR) // Invalid selection
254         return;
255 
256     ShutdownCode = SendDlgItemMessageW(hDlg, IDC_SHUTDOWN_ACTION, CB_GETITEMDATA, ShutdownCode, 0);
257 
258     switch (ShutdownCode)
259     {
260         case WLX_SAS_ACTION_LOGOFF:
261             DescId = IDS_SHUTDOWN_LOGOFF_DESC;
262             break;
263 
264         case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF:
265             DescId = IDS_SHUTDOWN_SHUTDOWN_DESC;
266             break;
267 
268         case WLX_SAS_ACTION_SHUTDOWN_REBOOT:
269             DescId = IDS_SHUTDOWN_RESTART_DESC;
270             break;
271 
272         case WLX_SAS_ACTION_SHUTDOWN_SLEEP:
273             DescId = IDS_SHUTDOWN_SLEEP_DESC;
274             break;
275 
276         case WLX_SAS_ACTION_SHUTDOWN_HIBERNATE:
277             DescId = IDS_SHUTDOWN_HIBERNATE_DESC;
278             break;
279 
280         default:
281             break;
282     }
283 
284     LoadStringW(pContext->pgContext->hDllInstance, DescId, szBuffer, _countof(szBuffer));
285     SetDlgItemTextW(hDlg, IDC_SHUTDOWN_DESCRIPTION, szBuffer);
286 
287     if (pContext->bReasonUI)
288     {
289         EnableWindow(GetDlgItem(hDlg, IDC_REASON_PLANNED), (ShutdownCode != WLX_SAS_ACTION_LOGOFF));
290         EnableWindow(GetDlgItem(hDlg, IDC_REASON_LIST), (ShutdownCode != WLX_SAS_ACTION_LOGOFF));
291         EnableWindow(GetDlgItem(hDlg, IDC_REASON_COMMENT), (ShutdownCode != WLX_SAS_ACTION_LOGOFF));
292     }
293 }
294 
295 static VOID
296 ShutdownOnInit(
297     IN HWND hDlg,
298     IN PSHUTDOWN_DLG_CONTEXT pContext)
299 {
300     PGINA_CONTEXT pgContext = pContext->pgContext;
301     HWND hwndList;
302     INT idx, count, i;
303     WCHAR szBuffer[256];
304     WCHAR szBuffer2[256];
305 
306     hwndList = GetDlgItem(hDlg, IDC_SHUTDOWN_ACTION);
307 
308     /* Clear the content before it's used */
309     SendMessageW(hwndList, CB_RESETCONTENT, 0, 0);
310 
311     /* Log off */
312     if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_LOGOFF)
313     {
314         LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_LOGOFF, szBuffer, _countof(szBuffer));
315         StringCchPrintfW(szBuffer2, _countof(szBuffer2), szBuffer, pgContext->UserName);
316         idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer2);
317         if (idx != CB_ERR)
318             SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_LOGOFF);
319     }
320 
321     /* Shut down - DEFAULT */
322     if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_POWER_OFF)
323     {
324         LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_SHUTDOWN, szBuffer, _countof(szBuffer));
325         idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer);
326         if (idx != CB_ERR)
327             SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_POWER_OFF);
328     }
329 
330     /* Restart */
331     if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_REBOOT)
332     {
333         LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_RESTART, szBuffer, _countof(szBuffer));
334         idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer);
335         if (idx != CB_ERR)
336             SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_REBOOT);
337     }
338 
339     // if (pContext->ShutdownOptions & 0x08) {}
340 
341     /* Sleep */
342     if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_SLEEP)
343     {
344         LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_SLEEP, szBuffer, _countof(szBuffer));
345         idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer);
346         if (idx != CB_ERR)
347             SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_SLEEP);
348     }
349 
350     // if (pContext->ShutdownOptions & 0x20) {}
351 
352     /* Hibernate */
353     if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_HIBERNATE)
354     {
355         LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_HIBERNATE, szBuffer, _countof(szBuffer));
356         idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer);
357         if (idx != CB_ERR)
358             SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_HIBERNATE);
359     }
360 
361     // if (pContext->ShutdownOptions & 0x80) {}
362 
363     /* Set the default shut down selection */
364     count = SendMessageW(hwndList, CB_GETCOUNT, 0, 0);
365     for (i = 0; i < count; i++)
366     {
367         if (SendMessageW(hwndList, CB_GETITEMDATA, i, 0) == pgContext->nShutdownAction)
368         {
369             SendMessageW(hwndList, CB_SETCURSEL, i, 0);
370             break;
371         }
372     }
373 
374     /* Update the choice description based on the current selection */
375     UpdateShutdownDesc(hDlg, pContext);
376 }
377 
378 static VOID
379 ShutdownOnOk(
380     IN HWND hDlg,
381     IN PGINA_CONTEXT pgContext)
382 {
383     INT idx;
384 
385     idx = SendDlgItemMessageW(hDlg,
386                               IDC_SHUTDOWN_ACTION,
387                               CB_GETCURSEL,
388                               0,
389                               0);
390     if (idx != CB_ERR)
391     {
392         pgContext->nShutdownAction =
393             SendDlgItemMessageW(hDlg,
394                                 IDC_SHUTDOWN_ACTION,
395                                 CB_GETITEMDATA,
396                                 idx,
397                                 0);
398     }
399 }
400 
401 static INT_PTR
402 CALLBACK
403 ShutdownDialogProc(
404     HWND hDlg,
405     UINT uMsg,
406     WPARAM wParam,
407     LPARAM lParam)
408 {
409     PSHUTDOWN_DLG_CONTEXT pContext;
410 
411     pContext = (PSHUTDOWN_DLG_CONTEXT)GetWindowLongPtrW(hDlg, GWLP_USERDATA);
412 
413     switch (uMsg)
414     {
415         case WM_INITDIALOG:
416         {
417             pContext = (PSHUTDOWN_DLG_CONTEXT)lParam;
418             SetWindowLongPtrW(hDlg, GWLP_USERDATA, (LONG_PTR)pContext);
419 
420             ShutdownOnInit(hDlg, pContext);
421 
422             /* Draw the logo bitmap */
423             pContext->hBitmap =
424                 LoadImageW(pContext->pgContext->hDllInstance, MAKEINTRESOURCEW(IDI_ROSLOGO), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
425             return TRUE;
426         }
427 
428         case WM_DESTROY:
429             DeleteObject(pContext->hBitmap);
430             return TRUE;
431 
432         case WM_ACTIVATE:
433         {
434             /*
435              * If the user deactivates the shutdown dialog (it loses its focus
436              * while the dialog is not being closed), then destroy the dialog
437              * and cancel shutdown.
438              */
439             if (LOWORD(wParam) == WA_INACTIVE)
440             {
441                 if (!pContext->bCloseDlg)
442                 {
443                     pContext->bCloseDlg = TRUE;
444                     EndDialog(hDlg, 0);
445                 }
446             }
447             return FALSE;
448         }
449 
450         case WM_PAINT:
451         {
452             PAINTSTRUCT ps;
453             if (pContext->hBitmap)
454             {
455                 BeginPaint(hDlg, &ps);
456                 DrawStateW(ps.hdc, NULL, NULL, (LPARAM)pContext->hBitmap, (WPARAM)0, 0, 0, 0, 0, DST_BITMAP);
457                 EndPaint(hDlg, &ps);
458             }
459             return TRUE;
460         }
461 
462         case WM_CLOSE:
463             pContext->bCloseDlg = TRUE;
464             EndDialog(hDlg, IDCANCEL);
465             break;
466 
467         case WM_COMMAND:
468             switch (LOWORD(wParam))
469             {
470                 case IDOK:
471                     ShutdownOnOk(hDlg, pContext->pgContext);
472 
473                 /* Fall back */
474                 case IDCANCEL:
475                 case IDHELP:
476                     pContext->bCloseDlg = TRUE;
477                     EndDialog(hDlg, LOWORD(wParam));
478                     break;
479 
480                 case IDC_SHUTDOWN_ACTION:
481                     UpdateShutdownDesc(hDlg, pContext);
482                     break;
483             }
484             break;
485 
486         default:
487             return FALSE;
488     }
489     return TRUE;
490 }
491 
492 INT_PTR
493 ShutdownDialog(
494     IN HWND hwndDlg,
495     IN DWORD ShutdownOptions,
496     IN PGINA_CONTEXT pgContext)
497 {
498     INT_PTR ret;
499     SHUTDOWN_DLG_CONTEXT Context;
500 
501 #if 0
502     DWORD ShutdownOptions;
503 
504     // FIXME: User impersonation!!
505     pgContext->nShutdownAction = LoadShutdownSelState();
506     ShutdownOptions = GetAllowedShutdownOptions();
507 #endif
508 
509     Context.pgContext = pgContext;
510     Context.ShutdownOptions = ShutdownOptions;
511     Context.bCloseDlg = FALSE;
512     Context.bReasonUI = GetShutdownReasonUI();
513 
514     if (pgContext->hWlx && pgContext->pWlxFuncs)
515     {
516         ret = pgContext->pWlxFuncs->WlxDialogBoxParam(pgContext->hWlx,
517                                                       pgContext->hDllInstance,
518                                                       MAKEINTRESOURCEW(Context.bReasonUI ? IDD_SHUTDOWN_REASON : IDD_SHUTDOWN),
519                                                       hwndDlg,
520                                                       ShutdownDialogProc,
521                                                       (LPARAM)&Context);
522     }
523     else
524     {
525         ret = DialogBoxParamW(pgContext->hDllInstance,
526                               MAKEINTRESOURCEW(Context.bReasonUI ? IDD_SHUTDOWN_REASON : IDD_SHUTDOWN),
527                               hwndDlg,
528                               ShutdownDialogProc,
529                               (LPARAM)&Context);
530     }
531 
532 #if 0
533     // FIXME: User impersonation!!
534     if (ret == IDOK)
535         SaveShutdownSelState(pgContext->nShutdownAction);
536 #endif
537 
538     return ret;
539 }
540 
541 
542 /*
543  * NOTES:
544  * - Based upon observations on the ShellShutdownDialog() function, the function doesn't actually
545  *   do anything except show a dialog box and returning a value based upon the value chosen. That
546  *   means that any code that calls the function has to execute the chosen action (shut down,
547  *   restart, etc.).
548  * - When this function is called in Windows XP, it shows the classic dialog box regardless if
549  *   SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\LogonType is enabled or not.
550  * - When the Help button is pushed, it sends the same return value as IDCANCEL (0x00), but
551  *   at the same time, it calls the help file directly from the dialog box.
552  * - When the dialog is created, it doesn't disable all other input from the other windows.
553  *   This is done elsewhere. When running the function ShellShutdownDialog() from XP/2K3, if the user clicks
554  *   out of the window, it automatically closes itself.
555  * - The parameter, lpUsername never seems to be used when calling the function from Windows XP. Either
556  *   it was a parameter that was never used in the final version before release, or it has a use that
557  *   is currently not known.
558  */
559 DWORD WINAPI
560 ShellShutdownDialog(
561     HWND   hParent,
562     LPWSTR lpUsername,
563     BOOL   bHideLogoff)
564 {
565     INT_PTR dlgValue;
566     DWORD ShutdownOptions;
567 
568     /*
569      * As we are called by the shell itself, don't use
570      * the cached GINA context but use a local copy here.
571      */
572     GINA_CONTEXT gContext = { 0 };
573     DWORD BufferSize;
574 
575     UNREFERENCED_PARAMETER(lpUsername);
576 
577     ShutdownOptions = GetAllowedShutdownOptions();
578     if (bHideLogoff)
579         ShutdownOptions &= ~WLX_SHUTDOWN_STATE_LOGOFF;
580 
581     /* Initialize our local GINA context */
582     gContext.hDllInstance = hDllInstance;
583     BufferSize = _countof(gContext.UserName);
584     // NOTE: Only when this function is called, Win checks inside
585     // HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
586     // value "Logon User Name", and determines whether it will display
587     // the user name.
588     GetUserNameW(gContext.UserName, &BufferSize);
589     gContext.nShutdownAction = LoadShutdownSelState();
590 
591     /* Load the shutdown dialog box */
592     dlgValue = ShutdownDialog(hParent, ShutdownOptions, &gContext);
593 
594     /* Determine what to do based on user selection */
595     if (dlgValue == IDOK)
596     {
597         SaveShutdownSelState(gContext.nShutdownAction);
598 
599         switch (gContext.nShutdownAction)
600         {
601             case WLX_SAS_ACTION_LOGOFF:
602                 return WLX_SHUTDOWN_STATE_LOGOFF;
603 
604             case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF:
605                 return WLX_SHUTDOWN_STATE_POWER_OFF;
606 
607             case WLX_SAS_ACTION_SHUTDOWN_REBOOT:
608                 return WLX_SHUTDOWN_STATE_REBOOT;
609 
610             // 0x08
611 
612             case WLX_SAS_ACTION_SHUTDOWN_SLEEP:
613                 return WLX_SHUTDOWN_STATE_SLEEP;
614 
615             // 0x20
616 
617             case WLX_SAS_ACTION_SHUTDOWN_HIBERNATE:
618                 return WLX_SHUTDOWN_STATE_HIBERNATE;
619 
620             // 0x80
621         }
622     }
623     /* Help file is called directly here */
624     else if (dlgValue == IDHELP)
625     {
626         FIXME("Help is not implemented yet.");
627         MessageBoxW(hParent, L"Help is not implemented yet.", L"Message", MB_OK | MB_ICONEXCLAMATION);
628     }
629     else if (dlgValue == -1)
630     {
631         ERR("Failed to create dialog\n");
632     }
633 
634     return 0;
635 }
636