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