xref: /reactos/dll/win32/msgina/shutdown.c (revision 1a102501)
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  *                  Arnav Bhatt (arnavbhatt288 at gmail dot com)
9  */
10 
11 #include "msgina.h"
12 #include <powrprof.h>
13 #include <wingdi.h>
14 #include <windowsx.h>
15 #include <commctrl.h>
16 
17 /* Shutdown state flags */
18 #define WLX_SHUTDOWN_STATE_LOGOFF       0x01
19 #define WLX_SHUTDOWN_STATE_POWER_OFF    0x02
20 #define WLX_SHUTDOWN_STATE_REBOOT       0x04
21 // 0x08
22 #define WLX_SHUTDOWN_STATE_SLEEP        0x10
23 // 0x20
24 #define WLX_SHUTDOWN_STATE_HIBERNATE    0x40
25 // 0x80
26 
27 /* Macros for fancy shut down dialog */
28 #define FONT_POINT_SIZE                 13
29 
30 #define DARK_GREY_COLOR                 RGB(244, 244, 244)
31 #define LIGHT_GREY_COLOR                RGB(38, 38, 38)
32 
33 /* Bitmap's size for buttons */
34 #define CX_BITMAP                       33
35 #define CY_BITMAP                       33
36 
37 #define NUMBER_OF_BUTTONS               4
38 
39 /* After determining the button as well as its state paint the image strip bitmap using these predefined positions */
40 #define BUTTON_SHUTDOWN                 0
41 #define BUTTON_SHUTDOWN_PRESSED         (CY_BITMAP + BUTTON_SHUTDOWN)
42 #define BUTTON_SHUTDOWN_FOCUSED         (CY_BITMAP + BUTTON_SHUTDOWN_PRESSED)
43 #define BUTTON_REBOOT                   (CY_BITMAP + BUTTON_SHUTDOWN_FOCUSED)
44 #define BUTTON_REBOOT_PRESSED           (CY_BITMAP + BUTTON_REBOOT)
45 #define BUTTON_REBOOT_FOCUSED           (CY_BITMAP + BUTTON_REBOOT_PRESSED)
46 #define BUTTON_SLEEP                    (CY_BITMAP + BUTTON_REBOOT_FOCUSED)
47 #define BUTTON_SLEEP_PRESSED            (CY_BITMAP + BUTTON_SLEEP)
48 #define BUTTON_SLEEP_FOCUSED            (CY_BITMAP + BUTTON_SLEEP_PRESSED)
49 #define BUTTON_SLEEP_DISABLED           (CY_BITMAP + BUTTON_SLEEP_FOCUSED)
50 
51 /* For bIsButtonHot */
52 #define SHUTDOWN_BUTTON_HOT             0
53 #define REBOOT_BUTTON_HOT               1
54 #define SLEEP_BUTTON_HOT                2
55 #define HIBERNATE_BUTTON_HOT            3
56 
57 typedef struct _SHUTDOWN_DLG_CONTEXT
58 {
59     PGINA_CONTEXT pgContext;
60     HBITMAP hBitmap;
61     HBITMAP hImageStrip;
62     DWORD ShutdownDialogId;
63     DWORD ShutdownOptions;
64     HBRUSH hBrush;
65     HFONT hfFont;
66     BOOL bCloseDlg;
67     BOOL bIsSleepButtonReplaced;
68     BOOL bReasonUI;
69     BOOL bFriendlyUI;
70     BOOL bIsButtonHot[NUMBER_OF_BUTTONS];
71     BOOL bTimer;
72     UINT_PTR iTimer;
73     WNDPROC OldButtonProc;
74 } SHUTDOWN_DLG_CONTEXT, *PSHUTDOWN_DLG_CONTEXT;
75 
76 static
77 BOOL
GetShutdownReasonUI(VOID)78 GetShutdownReasonUI(VOID)
79 {
80     OSVERSIONINFOEX VersionInfo;
81     DWORD dwValue, dwSize;
82     HKEY hKey;
83     LONG lRet;
84 
85     /* Query the policy value */
86     lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
87                          L"Software\\Policies\\Microsoft\\Windows NT\\Reliability",
88                          0,
89                          KEY_QUERY_VALUE,
90                          &hKey);
91     if (lRet == ERROR_SUCCESS)
92     {
93         dwValue = 0;
94         dwSize = sizeof(dwValue);
95         RegQueryValueExW(hKey,
96                          L"ShutdownReasonUI",
97                          NULL,
98                          NULL,
99                          (LPBYTE)&dwValue,
100                          &dwSize);
101         RegCloseKey(hKey);
102 
103         return (dwValue != 0) ? TRUE : FALSE;
104     }
105 
106     /* Query the machine value */
107     lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
108                          L"Software\\Microsoft\\Windows\\CurrentVersion\\Reliability",
109                          0,
110                          KEY_QUERY_VALUE,
111                          &hKey);
112     if (lRet == ERROR_SUCCESS)
113     {
114         dwValue = 0;
115         dwSize = sizeof(dwValue);
116         RegQueryValueExW(hKey,
117                          L"ShutdownReasonUI",
118                          NULL,
119                          NULL,
120                          (LPBYTE)&dwValue,
121                          &dwSize);
122         RegCloseKey(hKey);
123 
124         return (dwValue != 0) ? TRUE : FALSE;
125     }
126 
127     /* Return the default value */
128     VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo);
129     if (!GetVersionEx((POSVERSIONINFO)&VersionInfo))
130         return FALSE;
131 
132     return FALSE;
133 //    return (VersionInfo.wProductType == VER_NT_WORKSTATION) ? FALSE : TRUE;
134 }
135 
136 static
137 BOOL
IsFriendlyUIActive(VOID)138 IsFriendlyUIActive(VOID)
139 {
140     DWORD dwType, dwValue, dwSize;
141     HKEY hKey;
142     LONG lRet;
143 
144     lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
145                          L"SYSTEM\\CurrentControlSet\\Control\\Windows",
146                          0,
147                          KEY_QUERY_VALUE,
148                          &hKey);
149     if (lRet != ERROR_SUCCESS)
150         return FALSE;
151 
152     /* CORE-17282 First check an optional ReactOS specific override, that Windows does not check.
153        We use this to allow users pairing 'Server'-configuration with FriendlyShutdown.
154        Otherwise users would have to change CSDVersion or LogonType (side-effects AppCompat) */
155     dwValue = 0;
156     dwSize = sizeof(dwValue);
157     lRet = RegQueryValueExW(hKey,
158                             L"EnforceFriendlyShutdown",
159                             NULL,
160                             &dwType,
161                             (LPBYTE)&dwValue,
162                             &dwSize);
163 
164     if (lRet == ERROR_SUCCESS && dwType == REG_DWORD && dwValue == 0x1)
165     {
166         RegCloseKey(hKey);
167         return TRUE;
168     }
169 
170     /* Check product version number */
171     dwValue = 0;
172     dwSize = sizeof(dwValue);
173     lRet = RegQueryValueExW(hKey,
174                             L"CSDVersion",
175                             NULL,
176                             &dwType,
177                             (LPBYTE)&dwValue,
178                             &dwSize);
179     RegCloseKey(hKey);
180 
181     if (lRet != ERROR_SUCCESS || dwType != REG_DWORD || dwValue != 0x300)
182     {
183         /* Allow Friendly UI only on Workstation */
184         return FALSE;
185     }
186 
187     /* Check LogonType value */
188     lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
189                          L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
190                          0,
191                          KEY_QUERY_VALUE,
192                          &hKey);
193     if (lRet != ERROR_SUCCESS)
194         return FALSE;
195 
196     dwValue = 0;
197     dwSize = sizeof(dwValue);
198     lRet = RegQueryValueExW(hKey,
199                             L"LogonType",
200                             NULL,
201                             &dwType,
202                             (LPBYTE)&dwValue,
203                             &dwSize);
204     RegCloseKey(hKey);
205 
206     if (lRet != ERROR_SUCCESS || dwType != REG_DWORD)
207         return FALSE;
208 
209     return (dwValue != 0);
210 }
211 
212 static
213 BOOL
IsDomainMember(VOID)214 IsDomainMember(VOID)
215 {
216     UNIMPLEMENTED;
217     return FALSE;
218 }
219 
220 static
221 BOOL
IsNetwareActive(VOID)222 IsNetwareActive(VOID)
223 {
224     UNIMPLEMENTED;
225     return FALSE;
226 }
227 
228 static
229 BOOL
IsShowHibernateButtonActive(VOID)230 IsShowHibernateButtonActive(VOID)
231 {
232     INT_PTR lRet;
233     HKEY hKey;
234     DWORD dwValue, dwSize;
235 
236     lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
237                          L"SOFTWARE\\Policies\\Microsoft\\Windows\\System\\Shutdown",
238                          0, KEY_QUERY_VALUE, &hKey);
239     if (lRet == ERROR_SUCCESS)
240     {
241         dwValue = 0;
242         dwSize = sizeof(dwValue);
243 
244         lRet = RegQueryValueExW(hKey,
245                                 L"ShowHibernateButton",
246                                 NULL, NULL,
247                                 (LPBYTE)&dwValue, &dwSize);
248         RegCloseKey(hKey);
249         if (lRet != ERROR_SUCCESS)
250         {
251             return FALSE;
252         }
253         return (dwValue != 0);
254     }
255     return FALSE;
256 }
257 
258 static
259 BOOL
ForceFriendlyUI(VOID)260 ForceFriendlyUI(VOID)
261 {
262     DWORD dwType, dwValue, dwSize;
263     HKEY hKey;
264     LONG lRet;
265 
266     lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
267                          L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
268                          0,
269                          KEY_QUERY_VALUE,
270                          &hKey);
271     if (lRet == ERROR_SUCCESS)
272     {
273         dwValue = 0;
274         dwSize = sizeof(dwValue);
275         lRet = RegQueryValueExW(hKey,
276                                 L"ForceFriendlyUI",
277                                 NULL,
278                                 &dwType,
279                                 (LPBYTE)&dwValue,
280                                 &dwSize);
281         RegCloseKey(hKey);
282 
283         if (lRet == ERROR_SUCCESS && dwType == REG_DWORD)
284             return (dwValue != 0);
285     }
286 
287     lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
288                          L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
289                          0,
290                          KEY_QUERY_VALUE,
291                          &hKey);
292     if (lRet == ERROR_SUCCESS)
293     {
294         dwValue = 0;
295         dwSize = sizeof(dwValue);
296         lRet = RegQueryValueExW(hKey,
297                                 L"ForceFriendlyUI",
298                                 NULL,
299                                 &dwType,
300                                 (LPBYTE)&dwValue,
301                                 &dwSize);
302 
303         RegCloseKey(hKey);
304 
305         if (lRet == ERROR_SUCCESS && dwType == REG_DWORD)
306             return (dwValue != 0);
307     }
308 
309     return FALSE;
310 }
311 
312 static
313 BOOL
DrawIconOnOwnerDrawnButtons(DRAWITEMSTRUCT * pdis,PSHUTDOWN_DLG_CONTEXT pContext)314 DrawIconOnOwnerDrawnButtons(
315     DRAWITEMSTRUCT* pdis,
316     PSHUTDOWN_DLG_CONTEXT pContext)
317 {
318     BOOL bRet;
319     HDC hdcMem;
320     HBITMAP hbmOld;
321     int y;
322     RECT rect;
323 
324     hdcMem = CreateCompatibleDC(pdis->hDC);
325     hbmOld = SelectObject(hdcMem, pContext->hImageStrip);
326     rect = pdis->rcItem;
327 
328     /* Check the button ID for revelant bitmap to be used */
329     switch (pdis->CtlID)
330     {
331         case IDC_BUTTON_SHUTDOWN:
332         {
333             switch (pdis->itemAction)
334             {
335                 case ODA_DRAWENTIRE:
336                 case ODA_FOCUS:
337                 case ODA_SELECT:
338                 {
339                     y = BUTTON_SHUTDOWN;
340                     if (pdis->itemState & ODS_SELECTED)
341                     {
342                         y = BUTTON_SHUTDOWN_PRESSED;
343                     }
344                     else if (pContext->bIsButtonHot[SHUTDOWN_BUTTON_HOT] || (pdis->itemState & ODS_FOCUS))
345                     {
346                         y = BUTTON_SHUTDOWN_FOCUSED;
347                     }
348                     break;
349                 }
350             }
351             break;
352         }
353 
354         case IDC_BUTTON_REBOOT:
355         {
356             switch (pdis->itemAction)
357             {
358                 case ODA_DRAWENTIRE:
359                 case ODA_FOCUS:
360                 case ODA_SELECT:
361                 {
362                     y = BUTTON_REBOOT;
363                     if (pdis->itemState & ODS_SELECTED)
364                     {
365                         y = BUTTON_REBOOT_PRESSED;
366                     }
367                     else if (pContext->bIsButtonHot[REBOOT_BUTTON_HOT] || (pdis->itemState & ODS_FOCUS))
368                     {
369                         y = BUTTON_REBOOT_FOCUSED;
370                     }
371                     break;
372                 }
373             }
374             break;
375         }
376 
377         case IDC_BUTTON_HIBERNATE:
378         case IDC_BUTTON_SLEEP:
379         {
380             switch (pdis->itemAction)
381             {
382                 case ODA_DRAWENTIRE:
383                 case ODA_FOCUS:
384                 case ODA_SELECT:
385                 {
386                     y = BUTTON_SLEEP;
387                     if (pdis->itemState & ODS_DISABLED)
388                     {
389                         y = BUTTON_SLEEP_DISABLED;
390                     }
391                     else if (pdis->itemState & ODS_SELECTED)
392                     {
393                         y = BUTTON_SLEEP_PRESSED;
394                     }
395                     else if ((pdis->CtlID == IDC_BUTTON_SLEEP && pContext->bIsButtonHot[SLEEP_BUTTON_HOT]) ||
396                              (pdis->CtlID == IDC_BUTTON_HIBERNATE && pContext->bIsButtonHot[HIBERNATE_BUTTON_HOT]) ||
397                              (pdis->itemState & ODS_FOCUS))
398                     {
399                         y = BUTTON_SLEEP_FOCUSED;
400                     }
401                     break;
402                 }
403             }
404             break;
405         }
406     }
407 
408     /* Draw it on the required button */
409     bRet = BitBlt(pdis->hDC,
410                   (rect.right - rect.left - CX_BITMAP) / 2,
411                   (rect.bottom - rect.top - CY_BITMAP) / 2,
412                   CX_BITMAP, CY_BITMAP, hdcMem, 0, y, SRCCOPY);
413 
414     SelectObject(hdcMem, hbmOld);
415     DeleteDC(hdcMem);
416 
417     return bRet;
418 }
419 
420 BOOL
421 WINAPI
ShellIsFriendlyUIActive(VOID)422 ShellIsFriendlyUIActive(VOID)
423 {
424     BOOL bActive;
425 
426     bActive = IsFriendlyUIActive();
427 
428     if ((IsDomainMember() || IsNetwareActive()) && !ForceFriendlyUI())
429         return FALSE;
430 
431     return bActive;
432 }
433 
434 DWORD
GetDefaultShutdownSelState(VOID)435 GetDefaultShutdownSelState(VOID)
436 {
437     return WLX_SAS_ACTION_SHUTDOWN_POWER_OFF;
438 }
439 
440 DWORD
LoadShutdownSelState(VOID)441 LoadShutdownSelState(VOID)
442 {
443     LONG lRet;
444     HKEY hKeyCurrentUser, hKey;
445     DWORD dwValue, dwTemp, dwSize;
446 
447     /* Default to shutdown */
448     dwValue = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF;
449 
450     /* Open the current user HKCU key */
451     lRet = RegOpenCurrentUser(MAXIMUM_ALLOWED, &hKeyCurrentUser);
452     if (lRet == ERROR_SUCCESS)
453     {
454         /* Open the subkey */
455         lRet = RegOpenKeyExW(hKeyCurrentUser,
456                              L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
457                              0, KEY_QUERY_VALUE, &hKey);
458         RegCloseKey(hKeyCurrentUser);
459     }
460     if (lRet != ERROR_SUCCESS)
461         return dwValue;
462 
463     /* Read the value */
464     dwSize = sizeof(dwTemp);
465     lRet = RegQueryValueExW(hKey,
466                             L"Shutdown Setting",
467                             NULL, NULL,
468                             (LPBYTE)&dwTemp, &dwSize);
469     RegCloseKey(hKey);
470 
471     if (lRet == ERROR_SUCCESS)
472     {
473         switch (dwTemp)
474         {
475             case WLX_SHUTDOWN_STATE_LOGOFF:
476                 dwValue = WLX_SAS_ACTION_LOGOFF;
477                 break;
478 
479             case WLX_SHUTDOWN_STATE_POWER_OFF:
480                 dwValue = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF;
481                 break;
482 
483             case WLX_SHUTDOWN_STATE_REBOOT:
484                 dwValue = WLX_SAS_ACTION_SHUTDOWN_REBOOT;
485                 break;
486 
487             // 0x08
488 
489             case WLX_SHUTDOWN_STATE_SLEEP:
490                 dwValue = WLX_SAS_ACTION_SHUTDOWN_SLEEP;
491                 break;
492 
493             // 0x20
494 
495             case WLX_SHUTDOWN_STATE_HIBERNATE:
496                 dwValue = WLX_SAS_ACTION_SHUTDOWN_HIBERNATE;
497                 break;
498 
499             // 0x80
500         }
501     }
502 
503     return dwValue;
504 }
505 
506 static INT_PTR
507 CALLBACK
OwnerDrawButtonSubclass(HWND hButton,UINT uMsg,WPARAM wParam,LPARAM lParam)508 OwnerDrawButtonSubclass(
509     HWND hButton,
510     UINT uMsg,
511     WPARAM wParam,
512     LPARAM lParam)
513 {
514     PSHUTDOWN_DLG_CONTEXT pContext;
515     pContext = (PSHUTDOWN_DLG_CONTEXT)GetWindowLongPtrW(GetParent(hButton), GWLP_USERDATA);
516     int buttonID = GetDlgCtrlID(hButton);
517 
518     switch (uMsg)
519     {
520         case WM_MOUSEMOVE:
521         {
522             HWND hwndTarget;
523             POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
524 
525             if (GetCapture() != hButton)
526             {
527                 SetCapture(hButton);
528                 if (buttonID == IDC_BUTTON_SHUTDOWN)
529                 {
530                     pContext->bIsButtonHot[SHUTDOWN_BUTTON_HOT] = TRUE;
531                 }
532                 else if (buttonID == IDC_BUTTON_REBOOT)
533                 {
534                     pContext->bIsButtonHot[REBOOT_BUTTON_HOT] = TRUE;
535                 }
536                 else if (buttonID == IDC_BUTTON_SLEEP)
537                 {
538                     pContext->bIsButtonHot[SLEEP_BUTTON_HOT] = TRUE;
539                 }
540                 else if (buttonID == IDC_BUTTON_HIBERNATE)
541                 {
542                     pContext->bIsButtonHot[HIBERNATE_BUTTON_HOT] = TRUE;
543                 }
544                 SetCursor(LoadCursorW(NULL, IDC_HAND));
545             }
546 
547             ClientToScreen(hButton, &pt);
548             hwndTarget = WindowFromPoint(pt);
549 
550             if (hwndTarget != hButton)
551             {
552                 ReleaseCapture();
553                 if (buttonID == IDC_BUTTON_SHUTDOWN)
554                 {
555                     pContext->bIsButtonHot[SHUTDOWN_BUTTON_HOT] = FALSE;
556                 }
557                 else if (buttonID == IDC_BUTTON_REBOOT)
558                 {
559                     pContext->bIsButtonHot[REBOOT_BUTTON_HOT] = FALSE;
560                 }
561                 else if (buttonID == IDC_BUTTON_SLEEP)
562                 {
563                     pContext->bIsButtonHot[SLEEP_BUTTON_HOT] = FALSE;
564                 }
565                 else if (buttonID == IDC_BUTTON_HIBERNATE)
566                 {
567                     pContext->bIsButtonHot[HIBERNATE_BUTTON_HOT] = FALSE;
568                 }
569             }
570             InvalidateRect(hButton, NULL, FALSE);
571             break;
572         }
573 
574         /* Whenever one of the buttons gets the keyboard focus, set it as default button */
575         case WM_SETFOCUS:
576         {
577             SendMessageW(GetParent(hButton), DM_SETDEFID, buttonID, 0);
578             break;
579         }
580 
581         /* Otherwise, set IDCANCEL as default button */
582         case WM_KILLFOCUS:
583         {
584             SendMessageW(GetParent(hButton), DM_SETDEFID, IDCANCEL, 0);
585             break;
586         }
587     }
588     return CallWindowProcW(pContext->OldButtonProc, hButton, uMsg, wParam, lParam);
589 }
590 
591 VOID
CreateToolTipForButtons(int controlID,int detailID,HWND hDlg,int titleID,HINSTANCE hInst)592 CreateToolTipForButtons(
593     int controlID,
594     int detailID,
595     HWND hDlg,
596     int titleID,
597     HINSTANCE hInst)
598 {
599     HWND hwndTool, hwndTip;
600     WCHAR szBuffer[256];
601     TTTOOLINFOW tool;
602 
603     hwndTool = GetDlgItem(hDlg, controlID);
604 
605     tool.cbSize = sizeof(tool);
606     tool.hwnd = hDlg;
607     tool.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
608     tool.uId = (UINT_PTR)hwndTool;
609 
610     /* Create the tooltip */
611     hwndTip = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL,
612                              WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,
613                              CW_USEDEFAULT, CW_USEDEFAULT,
614                              CW_USEDEFAULT, CW_USEDEFAULT,
615                              hDlg, NULL, hInst, NULL);
616 
617     /* Associate the tooltip with the tool. */
618     LoadStringW(hInst, detailID, szBuffer, _countof(szBuffer));
619     tool.lpszText = szBuffer;
620     SendMessageW(hwndTip, TTM_ADDTOOLW, 0, (LPARAM)&tool);
621     LoadStringW(hInst, titleID, szBuffer, _countof(szBuffer));
622     SendMessageW(hwndTip, TTM_SETTITLEW, TTI_NONE, (LPARAM)szBuffer);
623     SendMessageW(hwndTip, TTM_SETMAXTIPWIDTH, 0, 250);
624 }
625 
626 VOID
EndFriendlyDialog(HWND hDlg,PSHUTDOWN_DLG_CONTEXT pContext)627 EndFriendlyDialog(
628     HWND hDlg,
629     PSHUTDOWN_DLG_CONTEXT pContext)
630 {
631     if (pContext->bTimer)
632     {
633         KillTimer(hDlg, pContext->iTimer);
634     }
635 
636     DeleteObject(pContext->hBitmap);
637     DeleteObject(pContext->hBrush);
638     DeleteObject(pContext->hImageStrip);
639     DeleteObject(pContext->hfFont);
640 
641     /* Remove the subclass from the buttons */
642     for (int i = 0; i < NUMBER_OF_BUTTONS; i++)
643     {
644         SetWindowLongPtrW(GetDlgItem(hDlg, IDC_BUTTON_SHUTDOWN + i),
645                           GWLP_WNDPROC,
646                           (LONG_PTR)pContext->OldButtonProc);
647     }
648 }
649 
650 VOID
ChangeRequiredButton(HWND hDlg,PSHUTDOWN_DLG_CONTEXT pContext)651 ChangeRequiredButton(
652     HWND hDlg,
653     PSHUTDOWN_DLG_CONTEXT pContext)
654 {
655     int destID = IDC_BUTTON_SLEEP;
656     int targetedID = IDC_BUTTON_HIBERNATE;
657     HWND hwndDest, hwndTarget;
658     RECT rect;
659     WCHAR szBuffer[30];
660 
661     /* If the sleep button has been already replaced earlier, bring sleep button back to its original position */
662     if (pContext->bIsSleepButtonReplaced)
663     {
664         destID = IDC_BUTTON_HIBERNATE;
665         targetedID = IDC_BUTTON_SLEEP;
666     }
667 
668     hwndDest = GetDlgItem(hDlg, destID);
669     hwndTarget = GetDlgItem(hDlg, targetedID);
670 
671     /* Get the position of the destination button */
672     GetWindowRect(hwndDest, &rect);
673 
674     /* Get the corrected translated coordinates which is relative to the client window */
675     MapWindowPoints(HWND_DESKTOP, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
676 
677     /* Set the position of targeted button and hide the destination button */
678     SetWindowPos(hwndTarget,
679                  HWND_TOP,
680                  rect.left, rect.top,
681                  0, 0,
682                  SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
683 
684     EnableWindow(hwndDest, FALSE);
685     ShowWindow(hwndDest, SW_HIDE);
686     EnableWindow(hwndTarget, TRUE);
687     ShowWindow(hwndTarget, SW_SHOW);
688     SetFocus(hwndTarget);
689 
690     if (!pContext->bIsSleepButtonReplaced)
691     {
692         LoadStringW(pContext->pgContext->hDllInstance, IDS_SHUTDOWN_HIBERNATE, szBuffer, _countof(szBuffer));
693         SetDlgItemTextW(hDlg, IDC_SLEEP_STATIC, szBuffer);
694     }
695     else
696     {
697         LoadStringW(pContext->pgContext->hDllInstance, IDS_SHUTDOWN_SLEEP, szBuffer, _countof(szBuffer));
698         SetDlgItemTextW(hDlg, IDC_SLEEP_STATIC, szBuffer);
699     }
700 
701     InvalidateRect(hDlg, NULL, FALSE);
702 }
703 
OnTimer(HWND hDlg,PSHUTDOWN_DLG_CONTEXT pContext)704 VOID OnTimer(
705     HWND hDlg,
706     PSHUTDOWN_DLG_CONTEXT pContext)
707 {
708     BOOL ReplaceButton = !!(GetKeyState(VK_SHIFT) & 0x8000);
709 
710     if (ReplaceButton && !pContext->bIsSleepButtonReplaced)
711     {
712         ChangeRequiredButton(hDlg, pContext);
713         pContext->bIsSleepButtonReplaced = TRUE;
714     }
715     else if (!ReplaceButton && pContext->bIsSleepButtonReplaced)
716     {
717         ChangeRequiredButton(hDlg, pContext);
718         pContext->bIsSleepButtonReplaced = FALSE;
719     }
720 }
721 
722 VOID
SaveShutdownSelState(IN DWORD ShutdownCode)723 SaveShutdownSelState(
724     IN DWORD ShutdownCode)
725 {
726     LONG lRet;
727     HKEY hKeyCurrentUser, hKey;
728     DWORD dwValue = 0;
729 
730     /* Open the current user HKCU key */
731     lRet = RegOpenCurrentUser(MAXIMUM_ALLOWED, &hKeyCurrentUser);
732     if (lRet == ERROR_SUCCESS)
733     {
734         /* Create the subkey */
735         lRet = RegCreateKeyExW(hKeyCurrentUser,
736                                L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
737                                0, NULL,
738                                REG_OPTION_NON_VOLATILE,
739                                KEY_SET_VALUE,
740                                NULL, &hKey, NULL);
741         RegCloseKey(hKeyCurrentUser);
742     }
743     if (lRet != ERROR_SUCCESS)
744         return;
745 
746     switch (ShutdownCode)
747     {
748         case WLX_SAS_ACTION_LOGOFF:
749             dwValue = WLX_SHUTDOWN_STATE_LOGOFF;
750             break;
751 
752         case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF:
753             dwValue = WLX_SHUTDOWN_STATE_POWER_OFF;
754             break;
755 
756         case WLX_SAS_ACTION_SHUTDOWN_REBOOT:
757             dwValue = WLX_SHUTDOWN_STATE_REBOOT;
758             break;
759 
760         case WLX_SAS_ACTION_SHUTDOWN_SLEEP:
761             dwValue = WLX_SHUTDOWN_STATE_SLEEP;
762             break;
763 
764         case WLX_SAS_ACTION_SHUTDOWN_HIBERNATE:
765             dwValue = WLX_SHUTDOWN_STATE_HIBERNATE;
766             break;
767     }
768 
769     RegSetValueExW(hKey,
770                    L"Shutdown Setting",
771                    0, REG_DWORD,
772                    (LPBYTE)&dwValue, sizeof(dwValue));
773     RegCloseKey(hKey);
774 }
775 
776 DWORD
GetDefaultShutdownOptions(VOID)777 GetDefaultShutdownOptions(VOID)
778 {
779     return WLX_SHUTDOWN_STATE_POWER_OFF | WLX_SHUTDOWN_STATE_REBOOT;
780 }
781 
782 DWORD
GetAllowedShutdownOptions(VOID)783 GetAllowedShutdownOptions(VOID)
784 {
785     DWORD Options = 0;
786 
787     // FIXME: Compute those options accordings to current user's rights!
788     Options |= WLX_SHUTDOWN_STATE_LOGOFF | WLX_SHUTDOWN_STATE_POWER_OFF | WLX_SHUTDOWN_STATE_REBOOT;
789 
790     if (IsPwrSuspendAllowed())
791         Options |= WLX_SHUTDOWN_STATE_SLEEP;
792 
793     if (IsPwrHibernateAllowed())
794         Options |= WLX_SHUTDOWN_STATE_HIBERNATE;
795 
796     return Options;
797 }
798 
799 static VOID
UpdateShutdownDesc(IN HWND hDlg,IN PSHUTDOWN_DLG_CONTEXT pContext)800 UpdateShutdownDesc(
801     IN HWND hDlg,
802     IN PSHUTDOWN_DLG_CONTEXT pContext) // HINSTANCE hInstance
803 {
804     UINT DescId = 0;
805     DWORD ShutdownCode;
806     WCHAR szBuffer[256];
807 
808     ShutdownCode = SendDlgItemMessageW(hDlg, IDC_SHUTDOWN_ACTION, CB_GETCURSEL, 0, 0);
809     if (ShutdownCode == CB_ERR) // Invalid selection
810         return;
811 
812     ShutdownCode = SendDlgItemMessageW(hDlg, IDC_SHUTDOWN_ACTION, CB_GETITEMDATA, ShutdownCode, 0);
813 
814     switch (ShutdownCode)
815     {
816         case WLX_SAS_ACTION_LOGOFF:
817             DescId = IDS_SHUTDOWN_LOGOFF_DESC;
818             break;
819 
820         case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF:
821             DescId = IDS_SHUTDOWN_SHUTDOWN_DESC;
822             break;
823 
824         case WLX_SAS_ACTION_SHUTDOWN_REBOOT:
825             DescId = IDS_SHUTDOWN_RESTART_DESC;
826             break;
827 
828         case WLX_SAS_ACTION_SHUTDOWN_SLEEP:
829             DescId = IDS_SHUTDOWN_SLEEP_DESC;
830             break;
831 
832         case WLX_SAS_ACTION_SHUTDOWN_HIBERNATE:
833             DescId = IDS_SHUTDOWN_HIBERNATE_DESC;
834             break;
835 
836         default:
837             break;
838     }
839 
840     LoadStringW(pContext->pgContext->hDllInstance, DescId, szBuffer, _countof(szBuffer));
841     SetDlgItemTextW(hDlg, IDC_SHUTDOWN_DESCRIPTION, szBuffer);
842 
843     if (pContext->bReasonUI)
844     {
845         EnableWindow(GetDlgItem(hDlg, IDC_REASON_PLANNED), (ShutdownCode != WLX_SAS_ACTION_LOGOFF));
846         EnableWindow(GetDlgItem(hDlg, IDC_REASON_LIST), (ShutdownCode != WLX_SAS_ACTION_LOGOFF));
847         EnableWindow(GetDlgItem(hDlg, IDC_REASON_COMMENT), (ShutdownCode != WLX_SAS_ACTION_LOGOFF));
848     }
849 }
850 
851 static VOID
ShutdownOnFriendlyInit(IN HWND hDlg,IN PSHUTDOWN_DLG_CONTEXT pContext)852 ShutdownOnFriendlyInit(
853     IN HWND hDlg,
854     IN PSHUTDOWN_DLG_CONTEXT pContext)
855 {
856     PGINA_CONTEXT pgContext = pContext->pgContext;
857     HDC hdc;
858     LONG lfHeight;
859 
860     /* Create font for the IDC_TURN_OFF_STATIC static control */
861     hdc = GetDC(hDlg);
862     lfHeight = -MulDiv(FONT_POINT_SIZE, GetDeviceCaps(hdc, LOGPIXELSY), 72);
863     ReleaseDC(hDlg, hdc);
864     pContext->hfFont = CreateFontW(lfHeight, 0, 0, 0, FW_MEDIUM, FALSE, 0, 0, 0, 0, 0, 0, 0, L"MS Shell Dlg");
865     SendDlgItemMessageW(hDlg, IDC_TURN_OFF_STATIC, WM_SETFONT, (WPARAM)pContext->hfFont, TRUE);
866 
867     /* Create a brush for static controls for fancy shut down dialog */
868     pContext->hBrush = CreateSolidBrush(DARK_GREY_COLOR);
869 
870     /* Gather image strip */
871     pContext->hImageStrip = LoadBitmapW(pgContext->hDllInstance, MAKEINTRESOURCEW(IDB_IMAGE_STRIP));
872 
873     /* Set the boolean flags to false */
874     pContext->bIsSleepButtonReplaced = FALSE;
875     pContext->bTimer = FALSE;
876 
877     EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_HIBERNATE), FALSE);
878     EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_SLEEP), IsPwrSuspendAllowed());
879 
880     /* Gather old button func */
881     pContext->OldButtonProc = (WNDPROC)GetWindowLongPtrW(GetDlgItem(hDlg, IDC_BUTTON_HIBERNATE), GWLP_WNDPROC);
882 
883     /* Set bIsButtonHot to false, create tooltips for each buttons, make buttons to remember pContext and subclass the buttons */
884     for (int i = 0; i < NUMBER_OF_BUTTONS; i++)
885     {
886         pContext->bIsButtonHot[i] = FALSE;
887         SetWindowLongPtrW(GetDlgItem(hDlg, IDC_BUTTON_SHUTDOWN + i),
888                           GWLP_WNDPROC,
889                           (LONG_PTR)OwnerDrawButtonSubclass);
890         CreateToolTipForButtons(IDC_BUTTON_SHUTDOWN + i,
891                                 IDS_SHUTDOWN_SHUTDOWN_DESC + i,
892                                 hDlg, IDS_SHUTDOWN_SHUTDOWN + i,
893                                 pContext->pgContext->hDllInstance);
894     }
895 
896     if (pContext->ShutdownDialogId == IDD_SHUTDOWN_FANCY && IsPwrSuspendAllowed())
897     {
898         pContext->iTimer = SetTimer(hDlg, 0, 50, NULL);
899         pContext->bTimer = TRUE;
900     }
901 }
902 
903 static VOID
ShutdownOnInit(IN HWND hDlg,IN PSHUTDOWN_DLG_CONTEXT pContext)904 ShutdownOnInit(
905     IN HWND hDlg,
906     IN PSHUTDOWN_DLG_CONTEXT pContext)
907 {
908     PGINA_CONTEXT pgContext = pContext->pgContext;
909     HWND hwndList;
910     INT idx, count, i;
911     WCHAR szBuffer[256];
912     WCHAR szBuffer2[256];
913 
914     if (pContext->bFriendlyUI)
915     {
916         ShutdownOnFriendlyInit(hDlg, pContext);
917         return;
918     }
919 
920     hwndList = GetDlgItem(hDlg, IDC_SHUTDOWN_ACTION);
921 
922     /* Clear the content before it's used */
923     SendMessageW(hwndList, CB_RESETCONTENT, 0, 0);
924 
925     /* Log off */
926     if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_LOGOFF)
927     {
928         LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_LOGOFF, szBuffer, _countof(szBuffer));
929         StringCchPrintfW(szBuffer2, _countof(szBuffer2), szBuffer, pgContext->UserName);
930         idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer2);
931         if (idx != CB_ERR)
932             SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_LOGOFF);
933     }
934 
935     /* Shut down - DEFAULT */
936     if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_POWER_OFF)
937     {
938         LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_SHUTDOWN, szBuffer, _countof(szBuffer));
939         idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer);
940         if (idx != CB_ERR)
941             SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_POWER_OFF);
942     }
943 
944     /* Restart */
945     if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_REBOOT)
946     {
947         LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_RESTART, szBuffer, _countof(szBuffer));
948         idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer);
949         if (idx != CB_ERR)
950             SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_REBOOT);
951     }
952 
953     // if (pContext->ShutdownOptions & 0x08) {}
954 
955     /* Sleep */
956     if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_SLEEP)
957     {
958         LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_SLEEP, szBuffer, _countof(szBuffer));
959         idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer);
960         if (idx != CB_ERR)
961             SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_SLEEP);
962     }
963 
964     // if (pContext->ShutdownOptions & 0x20) {}
965 
966     /* Hibernate */
967     if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_HIBERNATE)
968     {
969         LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_HIBERNATE, szBuffer, _countof(szBuffer));
970         idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer);
971         if (idx != CB_ERR)
972             SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_HIBERNATE);
973     }
974 
975     // if (pContext->ShutdownOptions & 0x80) {}
976 
977     /* Set the default shut down selection */
978     count = SendMessageW(hwndList, CB_GETCOUNT, 0, 0);
979     for (i = 0; i < count; i++)
980     {
981         if (SendMessageW(hwndList, CB_GETITEMDATA, i, 0) == pgContext->nShutdownAction)
982         {
983             SendMessageW(hwndList, CB_SETCURSEL, i, 0);
984             break;
985         }
986     }
987 
988     /* Update the choice description based on the current selection */
989     UpdateShutdownDesc(hDlg, pContext);
990 }
991 
992 static VOID
ShutdownOnOk(IN HWND hDlg,IN PGINA_CONTEXT pgContext)993 ShutdownOnOk(
994     IN HWND hDlg,
995     IN PGINA_CONTEXT pgContext)
996 {
997     INT idx;
998 
999     idx = SendDlgItemMessageW(hDlg,
1000                               IDC_SHUTDOWN_ACTION,
1001                               CB_GETCURSEL,
1002                               0,
1003                               0);
1004     if (idx != CB_ERR)
1005     {
1006         pgContext->nShutdownAction =
1007             SendDlgItemMessageW(hDlg,
1008                                 IDC_SHUTDOWN_ACTION,
1009                                 CB_GETITEMDATA,
1010                                 idx,
1011                                 0);
1012     }
1013 }
1014 
1015 static INT_PTR
1016 CALLBACK
ShutdownDialogProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)1017 ShutdownDialogProc(
1018     HWND hDlg,
1019     UINT uMsg,
1020     WPARAM wParam,
1021     LPARAM lParam)
1022 {
1023     PSHUTDOWN_DLG_CONTEXT pContext;
1024 
1025     pContext = (PSHUTDOWN_DLG_CONTEXT)GetWindowLongPtrW(hDlg, GWLP_USERDATA);
1026 
1027     switch (uMsg)
1028     {
1029         case WM_INITDIALOG:
1030         {
1031             pContext = (PSHUTDOWN_DLG_CONTEXT)lParam;
1032             SetWindowLongPtrW(hDlg, GWLP_USERDATA, (LONG_PTR)pContext);
1033 
1034             ShutdownOnInit(hDlg, pContext);
1035             return TRUE;
1036         }
1037 
1038         case WM_DESTROY:
1039             if (pContext->bFriendlyUI)
1040             {
1041                 EndFriendlyDialog(hDlg, pContext);
1042             }
1043             return TRUE;
1044 
1045         case WM_ACTIVATE:
1046         {
1047             /*
1048              * If the user deactivates the shutdown dialog (it loses its focus
1049              * while the dialog is not being closed), then destroy the dialog
1050              * and cancel shutdown.
1051              */
1052             if (LOWORD(wParam) == WA_INACTIVE)
1053             {
1054                 if (!pContext->bCloseDlg)
1055                 {
1056                     pContext->bCloseDlg = TRUE;
1057                     EndDialog(hDlg, IDCANCEL);
1058                 }
1059             }
1060             return FALSE;
1061         }
1062 
1063         case WM_CLOSE:
1064             pContext->bCloseDlg = TRUE;
1065             EndDialog(hDlg, IDCANCEL);
1066             break;
1067 
1068         case WM_COMMAND:
1069             switch (LOWORD(wParam))
1070             {
1071                 case IDC_BUTTON_SHUTDOWN:
1072                     ExitWindowsEx(EWX_SHUTDOWN, SHTDN_REASON_MAJOR_OTHER);
1073                     break;
1074 
1075                 case IDC_BUTTON_REBOOT:
1076                     ExitWindowsEx(EWX_REBOOT, SHTDN_REASON_MAJOR_OTHER);
1077                     break;
1078 
1079                 case IDC_BUTTON_SLEEP:
1080                     SetSuspendState(TRUE, TRUE, TRUE);
1081                     break;
1082 
1083                 case IDOK:
1084                     ShutdownOnOk(hDlg, pContext->pgContext);
1085 
1086                 /* Fall back */
1087                 case IDCANCEL:
1088                 case IDHELP:
1089                     pContext->bCloseDlg = TRUE;
1090                     EndDialog(hDlg, LOWORD(wParam));
1091                     break;
1092 
1093                 case IDC_SHUTDOWN_ACTION:
1094                     UpdateShutdownDesc(hDlg, pContext);
1095                     break;
1096             }
1097             break;
1098 
1099         case WM_CTLCOLORSTATIC:
1100         {
1101             /* Either make background transparent or fill it with color for required static controls */
1102             HDC hdcStatic = (HDC)wParam;
1103             UINT StaticID = (UINT)GetWindowLongPtrW((HWND)lParam, GWL_ID);
1104 
1105             switch (StaticID)
1106             {
1107                 case IDC_TURN_OFF_STATIC:
1108                    SetTextColor(hdcStatic, DARK_GREY_COLOR);
1109                    SetBkMode(hdcStatic, TRANSPARENT);
1110                    return (INT_PTR)GetStockObject(HOLLOW_BRUSH);
1111 
1112                 case IDC_HIBERNATE_STATIC:
1113                 case IDC_SHUTDOWN_STATIC:
1114                 case IDC_SLEEP_STATIC:
1115                 case IDC_RESTART_STATIC:
1116                     SetTextColor(hdcStatic, LIGHT_GREY_COLOR);
1117                     SetBkMode(hdcStatic, TRANSPARENT);
1118                     return (LONG_PTR)pContext->hBrush;
1119             }
1120             return FALSE;
1121         }
1122 
1123         case WM_DRAWITEM:
1124         {
1125             /* Draw bitmaps on required buttons */
1126             DRAWITEMSTRUCT* pdis = (DRAWITEMSTRUCT*)lParam;
1127             switch (pdis->CtlID)
1128             {
1129                 case IDC_BUTTON_SHUTDOWN:
1130                 case IDC_BUTTON_REBOOT:
1131                 case IDC_BUTTON_SLEEP:
1132                 case IDC_BUTTON_HIBERNATE:
1133                     return DrawIconOnOwnerDrawnButtons(pdis, pContext);
1134             }
1135             break;
1136         }
1137 
1138         case WM_TIMER:
1139             OnTimer(hDlg, pContext);
1140             return TRUE;
1141 
1142         default:
1143             return FALSE;
1144     }
1145     return TRUE;
1146 }
1147 
1148 INT_PTR
ShutdownDialog(IN HWND hwndDlg,IN DWORD ShutdownOptions,IN PGINA_CONTEXT pgContext)1149 ShutdownDialog(
1150     IN HWND hwndDlg,
1151     IN DWORD ShutdownOptions,
1152     IN PGINA_CONTEXT pgContext)
1153 {
1154     INT_PTR ret;
1155     SHUTDOWN_DLG_CONTEXT Context;
1156 
1157 #if 0
1158     DWORD ShutdownOptions;
1159 
1160     // FIXME: User impersonation!!
1161     pgContext->nShutdownAction = LoadShutdownSelState();
1162     ShutdownOptions = GetAllowedShutdownOptions();
1163 #endif
1164 
1165     Context.pgContext = pgContext;
1166     Context.ShutdownOptions = ShutdownOptions;
1167     Context.ShutdownDialogId = IDD_SHUTDOWN;
1168     Context.bCloseDlg = FALSE;
1169     Context.bReasonUI = GetShutdownReasonUI();
1170     Context.bFriendlyUI = ShellIsFriendlyUIActive();
1171 
1172     if (pgContext->hWlx && pgContext->pWlxFuncs && !Context.bFriendlyUI)
1173     {
1174         ret = pgContext->pWlxFuncs->WlxDialogBoxParam(pgContext->hWlx,
1175                                                       pgContext->hDllInstance,
1176                                                       MAKEINTRESOURCEW(Context.bReasonUI ? IDD_SHUTDOWN_REASON : IDD_SHUTDOWN),
1177                                                       hwndDlg,
1178                                                       ShutdownDialogProc,
1179                                                       (LPARAM)&Context);
1180     }
1181     else
1182     {
1183         if (Context.bFriendlyUI)
1184         {
1185             if (IsShowHibernateButtonActive())
1186             {
1187                 Context.ShutdownDialogId = IDD_SHUTDOWN_FANCY_LONG;
1188             }
1189             else
1190             {
1191                 Context.ShutdownDialogId = IDD_SHUTDOWN_FANCY;
1192             }
1193         }
1194 
1195         ret = DialogBoxParamW(pgContext->hDllInstance,
1196                               MAKEINTRESOURCEW(Context.bReasonUI ? IDD_SHUTDOWN_REASON : Context.ShutdownDialogId),
1197                               hwndDlg,
1198                               ShutdownDialogProc,
1199                               (LPARAM)&Context);
1200     }
1201 
1202 #if 0
1203     // FIXME: User impersonation!!
1204     if (ret == IDOK)
1205         SaveShutdownSelState(pgContext->nShutdownAction);
1206 #endif
1207 
1208     return ret;
1209 }
1210 
1211 
1212 /*
1213  * NOTES:
1214  * - Based upon observations on the ShellShutdownDialog() function, the function doesn't actually
1215  *   do anything except show a dialog box and returning a value based upon the value chosen. That
1216  *   means that any code that calls the function has to execute the chosen action (shut down,
1217  *   restart, etc.).
1218  * - When this function is called in Windows XP, it shows the classic dialog box regardless if
1219  *   SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\LogonType is enabled or not.
1220  * - When the Help button is pushed, it sends the same return value as IDCANCEL (0x00), but
1221  *   at the same time, it calls the help file directly from the dialog box.
1222  * - When the dialog is created, it doesn't disable all other input from the other windows.
1223  *   This is done elsewhere. When running the function ShellShutdownDialog() from XP/2K3, if the user clicks
1224  *   out of the window, it automatically closes itself.
1225  * - The parameter, lpUsername never seems to be used when calling the function from Windows XP. Either
1226  *   it was a parameter that was never used in the final version before release, or it has a use that
1227  *   is currently not known.
1228  */
1229 DWORD WINAPI
ShellShutdownDialog(HWND hParent,LPWSTR lpUsername,BOOL bHideLogoff)1230 ShellShutdownDialog(
1231     HWND   hParent,
1232     LPWSTR lpUsername,
1233     BOOL   bHideLogoff)
1234 {
1235     INT_PTR dlgValue;
1236     DWORD ShutdownOptions;
1237 
1238     /*
1239      * As we are called by the shell itself, don't use
1240      * the cached GINA context but use a local copy here.
1241      */
1242     GINA_CONTEXT gContext = { 0 };
1243     DWORD BufferSize;
1244 
1245     UNREFERENCED_PARAMETER(lpUsername);
1246 
1247     ShutdownOptions = GetAllowedShutdownOptions();
1248     if (bHideLogoff)
1249         ShutdownOptions &= ~WLX_SHUTDOWN_STATE_LOGOFF;
1250 
1251     /* Initialize our local GINA context */
1252     gContext.hDllInstance = hDllInstance;
1253     BufferSize = _countof(gContext.UserName);
1254     // NOTE: Only when this function is called, Win checks inside
1255     // HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
1256     // value "Logon User Name", and determines whether it will display
1257     // the user name.
1258     GetUserNameW(gContext.UserName, &BufferSize);
1259     gContext.nShutdownAction = LoadShutdownSelState();
1260 
1261     /* Load the shutdown dialog box */
1262     dlgValue = ShutdownDialog(hParent, ShutdownOptions, &gContext);
1263 
1264     /* Determine what to do based on user selection */
1265     if (dlgValue == IDOK)
1266     {
1267         SaveShutdownSelState(gContext.nShutdownAction);
1268 
1269         switch (gContext.nShutdownAction)
1270         {
1271             case WLX_SAS_ACTION_LOGOFF:
1272                 return WLX_SHUTDOWN_STATE_LOGOFF;
1273 
1274             case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF:
1275                 return WLX_SHUTDOWN_STATE_POWER_OFF;
1276 
1277             case WLX_SAS_ACTION_SHUTDOWN_REBOOT:
1278                 return WLX_SHUTDOWN_STATE_REBOOT;
1279 
1280             // 0x08
1281 
1282             case WLX_SAS_ACTION_SHUTDOWN_SLEEP:
1283                 return WLX_SHUTDOWN_STATE_SLEEP;
1284 
1285             // 0x20
1286 
1287             case WLX_SAS_ACTION_SHUTDOWN_HIBERNATE:
1288                 return WLX_SHUTDOWN_STATE_HIBERNATE;
1289 
1290             // 0x80
1291         }
1292     }
1293     /* Help file is called directly here */
1294     else if (dlgValue == IDHELP)
1295     {
1296         FIXME("Help is not implemented yet.\n");
1297         MessageBoxW(hParent, L"Help is not implemented yet.", L"Message", MB_OK | MB_ICONEXCLAMATION);
1298     }
1299     else if (dlgValue == -1)
1300     {
1301         ERR("Failed to create dialog\n");
1302     }
1303 
1304     return 0;
1305 }
1306 
1307 /*
1308  * NOTES:
1309  * - Undocumented, called from MS shell32.dll to show the turn off dialog.
1310  * - Seems to have the same purpose as ShellShutdownDialog.
1311  */
1312 DWORD WINAPI
ShellTurnOffDialog(HWND hWnd)1313 ShellTurnOffDialog(HWND hWnd)
1314 {
1315     return ShellShutdownDialog(hWnd, NULL, FALSE);
1316 }
1317