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