mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-09-02 04:13:34 +00:00
CORE-19104
- Use `NtPowerInformation()` and the `IS_PWR_*` macros instead.
- Fancy shutdown dialog:
* Enable or disable the hibernate/sleep buttons depending on
the previously-determined available shutdown options.
* Don't invoke `ExitWindowsEx()` or `SetSuspendState()` directly
within Msgina, but return a suitable `WLX_SAS_ACTION_SHUTDOWN_*`
value, like what's done by the classic dialog. The power action
proper is then performed by the caller of the shutdown dialog:
either Shell32 or Winlogon.
1451 lines
45 KiB
C
1451 lines
45 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS msgina.dll
|
|
* FILE: lib/msgina/shutdown.c
|
|
* PURPOSE: Shutdown Dialog Box (GUI only)
|
|
* PROGRAMMERS: Lee Schroeder (spaceseel at gmail dot com)
|
|
* Hermes Belusca-Maito ([email protected])
|
|
* Arnav Bhatt (arnavbhatt288 at gmail dot com)
|
|
*/
|
|
|
|
#include "msgina.h"
|
|
#include <wingdi.h>
|
|
#include <windowsx.h>
|
|
#include <commctrl.h>
|
|
#include <ndk/pofuncs.h>
|
|
|
|
/* Macros for fancy shutdown dialog */
|
|
#define FONT_POINT_SIZE 13
|
|
|
|
#define DARK_GREY_COLOR RGB(244, 244, 244)
|
|
#define LIGHT_GREY_COLOR RGB(38, 38, 38)
|
|
|
|
/* Bitmap's size for buttons */
|
|
#define CX_BITMAP 33
|
|
#define CY_BITMAP 33
|
|
|
|
#define NUMBER_OF_BUTTONS 4
|
|
|
|
/* After determining the button as well as its state paint the image strip bitmap using these predefined positions */
|
|
#define BUTTON_SHUTDOWN 0
|
|
#define BUTTON_SHUTDOWN_PRESSED (CY_BITMAP + BUTTON_SHUTDOWN)
|
|
#define BUTTON_SHUTDOWN_FOCUSED (CY_BITMAP + BUTTON_SHUTDOWN_PRESSED)
|
|
#define BUTTON_REBOOT (CY_BITMAP + BUTTON_SHUTDOWN_FOCUSED)
|
|
#define BUTTON_REBOOT_PRESSED (CY_BITMAP + BUTTON_REBOOT)
|
|
#define BUTTON_REBOOT_FOCUSED (CY_BITMAP + BUTTON_REBOOT_PRESSED)
|
|
#define BUTTON_SLEEP (CY_BITMAP + BUTTON_REBOOT_FOCUSED)
|
|
#define BUTTON_SLEEP_PRESSED (CY_BITMAP + BUTTON_SLEEP)
|
|
#define BUTTON_SLEEP_FOCUSED (CY_BITMAP + BUTTON_SLEEP_PRESSED)
|
|
#define BUTTON_SLEEP_DISABLED (CY_BITMAP + BUTTON_SLEEP_FOCUSED)
|
|
|
|
/* For bIsButtonHot */
|
|
#define SHUTDOWN_BUTTON_HOT 0
|
|
#define REBOOT_BUTTON_HOT 1
|
|
#define SLEEP_BUTTON_HOT 2
|
|
#define HIBERNATE_BUTTON_HOT 3
|
|
|
|
typedef struct _SHUTDOWN_DLG_CONTEXT
|
|
{
|
|
PGINA_CONTEXT pgContext;
|
|
HBITMAP hBitmap;
|
|
HBITMAP hImageStrip;
|
|
DWORD ShutdownDialogId;
|
|
DWORD ShutdownOptions;
|
|
HBRUSH hBrush;
|
|
HFONT hfFont;
|
|
BOOL bCloseDlg;
|
|
BOOL bIsSleepButtonReplaced;
|
|
BOOL bReasonUI;
|
|
BOOL bFriendlyUI;
|
|
BOOL bIsButtonHot[NUMBER_OF_BUTTONS];
|
|
BOOL bTimer;
|
|
UINT_PTR iTimer;
|
|
WNDPROC OldButtonProc;
|
|
} SHUTDOWN_DLG_CONTEXT, *PSHUTDOWN_DLG_CONTEXT;
|
|
|
|
|
|
/**
|
|
* @brief
|
|
* Looks up a policy value on both HKEY_LOCAL_MACHINE
|
|
* and HKEY_CURRENT_USER, under the same policy sub-key.
|
|
*
|
|
* @note
|
|
* HKEY_LOCAL_MACHINE policies trump over HKEY_CURRENT_USER ones.
|
|
**/
|
|
LONG
|
|
GetPolicyDWORDValue(
|
|
_In_opt_ HKEY hKeyCurrentUser,
|
|
_In_ PCWSTR pPolicyKey,
|
|
_In_ PCWSTR ValueName,
|
|
_Out_ PDWORD pValue)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwValue;
|
|
LONG rc;
|
|
|
|
*pValue = 0;
|
|
|
|
/* Query the policy value in HKEY_LOCAL_MACHINE */
|
|
rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, pPolicyKey, 0,
|
|
KEY_QUERY_VALUE, &hKey);
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
rc = ReadRegDwordValue(hKey, ValueName, &dwValue);
|
|
RegCloseKey(hKey);
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
*pValue = dwValue;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/* If not found, query the policy value in HKEY_CURRENT_USER */
|
|
rc = RegOpenKeyExW(hKeyCurrentUser ? hKeyCurrentUser : HKEY_CURRENT_USER,
|
|
pPolicyKey, 0,
|
|
KEY_QUERY_VALUE, &hKey);
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
rc = ReadRegDwordValue(hKey, ValueName, &dwValue);
|
|
RegCloseKey(hKey);
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
*pValue = dwValue;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return ERROR_NOT_FOUND;
|
|
}
|
|
|
|
|
|
static
|
|
BOOL
|
|
GetShutdownReasonUI(VOID)
|
|
{
|
|
OSVERSIONINFOEX VersionInfo;
|
|
DWORD dwValue, dwSize;
|
|
HKEY hKey;
|
|
LONG lRet;
|
|
|
|
/* Query the policy value */
|
|
lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"Software\\Policies\\Microsoft\\Windows NT\\Reliability",
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey);
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
dwValue = 0;
|
|
dwSize = sizeof(dwValue);
|
|
RegQueryValueExW(hKey,
|
|
L"ShutdownReasonUI",
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize);
|
|
RegCloseKey(hKey);
|
|
|
|
return (dwValue != 0) ? TRUE : FALSE;
|
|
}
|
|
|
|
/* Query the machine value */
|
|
lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows\\CurrentVersion\\Reliability",
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey);
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
dwValue = 0;
|
|
dwSize = sizeof(dwValue);
|
|
RegQueryValueExW(hKey,
|
|
L"ShutdownReasonUI",
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize);
|
|
RegCloseKey(hKey);
|
|
|
|
return (dwValue != 0) ? TRUE : FALSE;
|
|
}
|
|
|
|
/* Return the default value */
|
|
VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo);
|
|
if (!GetVersionEx((POSVERSIONINFO)&VersionInfo))
|
|
return FALSE;
|
|
|
|
return FALSE;
|
|
// return (VersionInfo.wProductType == VER_NT_WORKSTATION) ? FALSE : TRUE;
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
IsFriendlyUIActive(VOID)
|
|
{
|
|
DWORD dwType, dwValue, dwSize;
|
|
HKEY hKey;
|
|
LONG lRet;
|
|
|
|
lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"SYSTEM\\CurrentControlSet\\Control\\Windows",
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey);
|
|
if (lRet != ERROR_SUCCESS)
|
|
return FALSE;
|
|
|
|
/* CORE-17282 First check an optional ReactOS specific override, that Windows does not check.
|
|
We use this to allow users pairing 'Server'-configuration with FriendlyShutdown.
|
|
Otherwise users would have to change CSDVersion or LogonType (side-effects AppCompat) */
|
|
dwValue = 0;
|
|
dwSize = sizeof(dwValue);
|
|
lRet = RegQueryValueExW(hKey,
|
|
L"EnforceFriendlyShutdown",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize);
|
|
|
|
if (lRet == ERROR_SUCCESS && dwType == REG_DWORD && dwValue == 0x1)
|
|
{
|
|
RegCloseKey(hKey);
|
|
return TRUE;
|
|
}
|
|
|
|
/* Check product version number */
|
|
dwValue = 0;
|
|
dwSize = sizeof(dwValue);
|
|
lRet = RegQueryValueExW(hKey,
|
|
L"CSDVersion",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize);
|
|
RegCloseKey(hKey);
|
|
|
|
if (lRet != ERROR_SUCCESS || dwType != REG_DWORD || dwValue != 0x300)
|
|
{
|
|
/* Allow Friendly UI only on Workstation */
|
|
return FALSE;
|
|
}
|
|
|
|
/* Check LogonType value */
|
|
lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey);
|
|
if (lRet != ERROR_SUCCESS)
|
|
return FALSE;
|
|
|
|
dwValue = 0;
|
|
dwSize = sizeof(dwValue);
|
|
lRet = RegQueryValueExW(hKey,
|
|
L"LogonType",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize);
|
|
RegCloseKey(hKey);
|
|
|
|
if (lRet != ERROR_SUCCESS || dwType != REG_DWORD)
|
|
return FALSE;
|
|
|
|
return (dwValue != 0);
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
IsDomainMember(VOID)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
IsNetwareActive(VOID)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
IsShowHibernateButtonActive(VOID)
|
|
{
|
|
INT_PTR lRet;
|
|
HKEY hKey;
|
|
DWORD dwValue, dwSize;
|
|
|
|
lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"SOFTWARE\\Policies\\Microsoft\\Windows\\System\\Shutdown",
|
|
0, KEY_QUERY_VALUE, &hKey);
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
dwValue = 0;
|
|
dwSize = sizeof(dwValue);
|
|
|
|
lRet = RegQueryValueExW(hKey,
|
|
L"ShowHibernateButton",
|
|
NULL, NULL,
|
|
(LPBYTE)&dwValue, &dwSize);
|
|
RegCloseKey(hKey);
|
|
if (lRet != ERROR_SUCCESS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
return (dwValue != 0);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
ForceFriendlyUI(VOID)
|
|
{
|
|
DWORD dwType, dwValue, dwSize;
|
|
HKEY hKey;
|
|
LONG lRet;
|
|
|
|
lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey);
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
dwValue = 0;
|
|
dwSize = sizeof(dwValue);
|
|
lRet = RegQueryValueExW(hKey,
|
|
L"ForceFriendlyUI",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize);
|
|
RegCloseKey(hKey);
|
|
|
|
if (lRet == ERROR_SUCCESS && dwType == REG_DWORD)
|
|
return (dwValue != 0);
|
|
}
|
|
|
|
lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey);
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
dwValue = 0;
|
|
dwSize = sizeof(dwValue);
|
|
lRet = RegQueryValueExW(hKey,
|
|
L"ForceFriendlyUI",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize);
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
if (lRet == ERROR_SUCCESS && dwType == REG_DWORD)
|
|
return (dwValue != 0);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
DrawIconOnOwnerDrawnButtons(
|
|
DRAWITEMSTRUCT* pdis,
|
|
PSHUTDOWN_DLG_CONTEXT pContext)
|
|
{
|
|
BOOL bRet;
|
|
HDC hdcMem;
|
|
HBITMAP hbmOld;
|
|
int y;
|
|
RECT rect;
|
|
|
|
hdcMem = CreateCompatibleDC(pdis->hDC);
|
|
hbmOld = SelectObject(hdcMem, pContext->hImageStrip);
|
|
rect = pdis->rcItem;
|
|
|
|
/* Check the button ID for relevant bitmap to be used */
|
|
switch (pdis->CtlID)
|
|
{
|
|
case IDC_BUTTON_SHUTDOWN:
|
|
{
|
|
switch (pdis->itemAction)
|
|
{
|
|
case ODA_DRAWENTIRE:
|
|
case ODA_FOCUS:
|
|
case ODA_SELECT:
|
|
{
|
|
y = BUTTON_SHUTDOWN;
|
|
if (pdis->itemState & ODS_SELECTED)
|
|
{
|
|
y = BUTTON_SHUTDOWN_PRESSED;
|
|
}
|
|
else if (pContext->bIsButtonHot[SHUTDOWN_BUTTON_HOT] || (pdis->itemState & ODS_FOCUS))
|
|
{
|
|
y = BUTTON_SHUTDOWN_FOCUSED;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IDC_BUTTON_REBOOT:
|
|
{
|
|
switch (pdis->itemAction)
|
|
{
|
|
case ODA_DRAWENTIRE:
|
|
case ODA_FOCUS:
|
|
case ODA_SELECT:
|
|
{
|
|
y = BUTTON_REBOOT;
|
|
if (pdis->itemState & ODS_SELECTED)
|
|
{
|
|
y = BUTTON_REBOOT_PRESSED;
|
|
}
|
|
else if (pContext->bIsButtonHot[REBOOT_BUTTON_HOT] || (pdis->itemState & ODS_FOCUS))
|
|
{
|
|
y = BUTTON_REBOOT_FOCUSED;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IDC_BUTTON_HIBERNATE:
|
|
case IDC_BUTTON_SLEEP:
|
|
{
|
|
switch (pdis->itemAction)
|
|
{
|
|
case ODA_DRAWENTIRE:
|
|
case ODA_FOCUS:
|
|
case ODA_SELECT:
|
|
{
|
|
y = BUTTON_SLEEP;
|
|
if (pdis->itemState & ODS_DISABLED)
|
|
{
|
|
y = BUTTON_SLEEP_DISABLED;
|
|
}
|
|
else if (pdis->itemState & ODS_SELECTED)
|
|
{
|
|
y = BUTTON_SLEEP_PRESSED;
|
|
}
|
|
else if ((pdis->CtlID == IDC_BUTTON_SLEEP && pContext->bIsButtonHot[SLEEP_BUTTON_HOT]) ||
|
|
(pdis->CtlID == IDC_BUTTON_HIBERNATE && pContext->bIsButtonHot[HIBERNATE_BUTTON_HOT]) ||
|
|
(pdis->itemState & ODS_FOCUS))
|
|
{
|
|
y = BUTTON_SLEEP_FOCUSED;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Draw it on the required button */
|
|
bRet = BitBlt(pdis->hDC,
|
|
(rect.right - rect.left - CX_BITMAP) / 2,
|
|
(rect.bottom - rect.top - CY_BITMAP) / 2,
|
|
CX_BITMAP, CY_BITMAP, hdcMem, 0, y, SRCCOPY);
|
|
|
|
SelectObject(hdcMem, hbmOld);
|
|
DeleteDC(hdcMem);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
ShellIsFriendlyUIActive(VOID)
|
|
{
|
|
BOOL bActive;
|
|
|
|
bActive = IsFriendlyUIActive();
|
|
|
|
if ((IsDomainMember() || IsNetwareActive()) && !ForceFriendlyUI())
|
|
return FALSE;
|
|
|
|
return bActive;
|
|
}
|
|
|
|
static INT_PTR
|
|
CALLBACK
|
|
OwnerDrawButtonSubclass(
|
|
HWND hButton,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
PSHUTDOWN_DLG_CONTEXT pContext;
|
|
pContext = (PSHUTDOWN_DLG_CONTEXT)GetWindowLongPtrW(GetParent(hButton), GWLP_USERDATA);
|
|
int buttonID = GetDlgCtrlID(hButton);
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
HWND hwndTarget;
|
|
POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
|
|
|
|
if (GetCapture() != hButton)
|
|
{
|
|
SetCapture(hButton);
|
|
if (buttonID == IDC_BUTTON_SHUTDOWN)
|
|
{
|
|
pContext->bIsButtonHot[SHUTDOWN_BUTTON_HOT] = TRUE;
|
|
}
|
|
else if (buttonID == IDC_BUTTON_REBOOT)
|
|
{
|
|
pContext->bIsButtonHot[REBOOT_BUTTON_HOT] = TRUE;
|
|
}
|
|
else if (buttonID == IDC_BUTTON_SLEEP)
|
|
{
|
|
pContext->bIsButtonHot[SLEEP_BUTTON_HOT] = TRUE;
|
|
}
|
|
else if (buttonID == IDC_BUTTON_HIBERNATE)
|
|
{
|
|
pContext->bIsButtonHot[HIBERNATE_BUTTON_HOT] = TRUE;
|
|
}
|
|
SetCursor(LoadCursorW(NULL, IDC_HAND));
|
|
}
|
|
|
|
ClientToScreen(hButton, &pt);
|
|
hwndTarget = WindowFromPoint(pt);
|
|
|
|
if (hwndTarget != hButton)
|
|
{
|
|
ReleaseCapture();
|
|
if (buttonID == IDC_BUTTON_SHUTDOWN)
|
|
{
|
|
pContext->bIsButtonHot[SHUTDOWN_BUTTON_HOT] = FALSE;
|
|
}
|
|
else if (buttonID == IDC_BUTTON_REBOOT)
|
|
{
|
|
pContext->bIsButtonHot[REBOOT_BUTTON_HOT] = FALSE;
|
|
}
|
|
else if (buttonID == IDC_BUTTON_SLEEP)
|
|
{
|
|
pContext->bIsButtonHot[SLEEP_BUTTON_HOT] = FALSE;
|
|
}
|
|
else if (buttonID == IDC_BUTTON_HIBERNATE)
|
|
{
|
|
pContext->bIsButtonHot[HIBERNATE_BUTTON_HOT] = FALSE;
|
|
}
|
|
}
|
|
InvalidateRect(hButton, NULL, FALSE);
|
|
break;
|
|
}
|
|
|
|
/* Whenever one of the buttons gets the keyboard focus, set it as default button */
|
|
case WM_SETFOCUS:
|
|
{
|
|
SendMessageW(GetParent(hButton), DM_SETDEFID, buttonID, 0);
|
|
break;
|
|
}
|
|
|
|
/* Otherwise, set IDCANCEL as default button */
|
|
case WM_KILLFOCUS:
|
|
{
|
|
SendMessageW(GetParent(hButton), DM_SETDEFID, IDCANCEL, 0);
|
|
break;
|
|
}
|
|
}
|
|
return CallWindowProcW(pContext->OldButtonProc, hButton, uMsg, wParam, lParam);
|
|
}
|
|
|
|
VOID
|
|
CreateToolTipForButtons(
|
|
int controlID,
|
|
int detailID,
|
|
HWND hDlg,
|
|
int titleID,
|
|
HINSTANCE hInst)
|
|
{
|
|
HWND hwndTool, hwndTip;
|
|
WCHAR szBuffer[256];
|
|
TTTOOLINFOW tool;
|
|
|
|
hwndTool = GetDlgItem(hDlg, controlID);
|
|
|
|
tool.cbSize = sizeof(tool);
|
|
tool.hwnd = hDlg;
|
|
tool.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
|
|
tool.uId = (UINT_PTR)hwndTool;
|
|
|
|
/* Create the tooltip */
|
|
hwndTip = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL,
|
|
WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
hDlg, NULL, hInst, NULL);
|
|
|
|
/* Associate the tooltip with the tool */
|
|
LoadStringW(hInst, detailID, szBuffer, _countof(szBuffer));
|
|
tool.lpszText = szBuffer;
|
|
SendMessageW(hwndTip, TTM_ADDTOOLW, 0, (LPARAM)&tool);
|
|
LoadStringW(hInst, titleID, szBuffer, _countof(szBuffer));
|
|
SendMessageW(hwndTip, TTM_SETTITLEW, TTI_NONE, (LPARAM)szBuffer);
|
|
SendMessageW(hwndTip, TTM_SETMAXTIPWIDTH, 0, 250);
|
|
}
|
|
|
|
VOID
|
|
EndFriendlyDialog(
|
|
HWND hDlg,
|
|
PSHUTDOWN_DLG_CONTEXT pContext)
|
|
{
|
|
if (pContext->bTimer)
|
|
{
|
|
KillTimer(hDlg, pContext->iTimer);
|
|
}
|
|
|
|
DeleteObject(pContext->hBitmap);
|
|
DeleteObject(pContext->hBrush);
|
|
DeleteObject(pContext->hImageStrip);
|
|
DeleteObject(pContext->hfFont);
|
|
|
|
/* Remove the subclass from the buttons */
|
|
for (int i = 0; i < NUMBER_OF_BUTTONS; i++)
|
|
{
|
|
SetWindowLongPtrW(GetDlgItem(hDlg, IDC_BUTTON_SHUTDOWN + i),
|
|
GWLP_WNDPROC,
|
|
(LONG_PTR)pContext->OldButtonProc);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
ChangeRequiredButton(
|
|
HWND hDlg,
|
|
PSHUTDOWN_DLG_CONTEXT pContext)
|
|
{
|
|
int destID = IDC_BUTTON_SLEEP;
|
|
int targetedID = IDC_BUTTON_HIBERNATE;
|
|
HWND hwndDest, hwndTarget;
|
|
RECT rect;
|
|
WCHAR szBuffer[30];
|
|
|
|
/* If the sleep button has been already replaced earlier, bring sleep button back to its original position */
|
|
if (pContext->bIsSleepButtonReplaced)
|
|
{
|
|
destID = IDC_BUTTON_HIBERNATE;
|
|
targetedID = IDC_BUTTON_SLEEP;
|
|
}
|
|
|
|
hwndDest = GetDlgItem(hDlg, destID);
|
|
hwndTarget = GetDlgItem(hDlg, targetedID);
|
|
|
|
/* Get the position of the destination button */
|
|
GetWindowRect(hwndDest, &rect);
|
|
|
|
/* Get the corrected translated coordinates which is relative to the client window */
|
|
MapWindowPoints(HWND_DESKTOP, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
|
|
|
|
/* Set the position of targeted button and hide the destination button */
|
|
SetWindowPos(hwndTarget,
|
|
HWND_TOP,
|
|
rect.left, rect.top,
|
|
0, 0,
|
|
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
EnableWindow(hwndDest, FALSE);
|
|
ShowWindow(hwndDest, SW_HIDE);
|
|
EnableWindow(hwndTarget, TRUE);
|
|
ShowWindow(hwndTarget, SW_SHOW);
|
|
SetFocus(hwndTarget);
|
|
|
|
if (!pContext->bIsSleepButtonReplaced)
|
|
{
|
|
LoadStringW(pContext->pgContext->hDllInstance, IDS_SHUTDOWN_HIBERNATE, szBuffer, _countof(szBuffer));
|
|
SetDlgItemTextW(hDlg, IDC_SLEEP_STATIC, szBuffer);
|
|
}
|
|
else
|
|
{
|
|
LoadStringW(pContext->pgContext->hDllInstance, IDS_SHUTDOWN_SLEEP, szBuffer, _countof(szBuffer));
|
|
SetDlgItemTextW(hDlg, IDC_SLEEP_STATIC, szBuffer);
|
|
}
|
|
|
|
InvalidateRect(hDlg, NULL, FALSE);
|
|
}
|
|
|
|
VOID OnTimer(
|
|
HWND hDlg,
|
|
PSHUTDOWN_DLG_CONTEXT pContext)
|
|
{
|
|
BOOL ReplaceButton = !!(GetKeyState(VK_SHIFT) & 0x8000);
|
|
|
|
if (ReplaceButton && !pContext->bIsSleepButtonReplaced)
|
|
{
|
|
ChangeRequiredButton(hDlg, pContext);
|
|
pContext->bIsSleepButtonReplaced = TRUE;
|
|
}
|
|
else if (!ReplaceButton && pContext->bIsSleepButtonReplaced)
|
|
{
|
|
ChangeRequiredButton(hDlg, pContext);
|
|
pContext->bIsSleepButtonReplaced = FALSE;
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
LoadShutdownSelState(
|
|
_In_ HKEY hKeyCurrentUser)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwValue, dwTemp, dwSize;
|
|
LONG lRet;
|
|
|
|
/* Default to first item */
|
|
dwValue = 0;
|
|
|
|
/* Open the subkey */
|
|
lRet = RegOpenKeyExW(hKeyCurrentUser,
|
|
L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
|
|
0, KEY_QUERY_VALUE, &hKey);
|
|
if (lRet != ERROR_SUCCESS)
|
|
return dwValue;
|
|
|
|
/* Read the value */
|
|
dwSize = sizeof(dwTemp);
|
|
lRet = RegQueryValueExW(hKey,
|
|
L"Shutdown Setting",
|
|
NULL, NULL,
|
|
(LPBYTE)&dwTemp, &dwSize);
|
|
RegCloseKey(hKey);
|
|
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
switch (dwTemp)
|
|
{
|
|
case WLX_SHUTDOWN_STATE_LOGOFF:
|
|
dwValue = WLX_SAS_ACTION_LOGOFF;
|
|
break;
|
|
|
|
case WLX_SHUTDOWN_STATE_POWER_OFF:
|
|
dwValue = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF;
|
|
break;
|
|
|
|
case WLX_SHUTDOWN_STATE_REBOOT:
|
|
dwValue = WLX_SAS_ACTION_SHUTDOWN_REBOOT;
|
|
break;
|
|
|
|
// 0x08
|
|
|
|
case WLX_SHUTDOWN_STATE_SLEEP:
|
|
dwValue = WLX_SAS_ACTION_SHUTDOWN_SLEEP;
|
|
break;
|
|
|
|
case WLX_SHUTDOWN_STATE_SLEEP2:
|
|
dwValue = WLX_SAS_ACTION_SHUTDOWN_SLEEP2;
|
|
break;
|
|
|
|
case WLX_SHUTDOWN_STATE_HIBERNATE:
|
|
dwValue = WLX_SAS_ACTION_SHUTDOWN_HIBERNATE;
|
|
break;
|
|
|
|
// WLX_SHUTDOWN_STATE_DISCONNECT
|
|
}
|
|
}
|
|
|
|
return dwValue;
|
|
}
|
|
|
|
VOID
|
|
SaveShutdownSelState(
|
|
_In_ HKEY hKeyCurrentUser,
|
|
_In_ DWORD ShutdownCode)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwValue = 0;
|
|
LONG lRet;
|
|
|
|
/* Create the subkey */
|
|
lRet = RegCreateKeyExW(hKeyCurrentUser,
|
|
L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
|
|
0, NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_SET_VALUE,
|
|
NULL, &hKey, NULL);
|
|
if (lRet != ERROR_SUCCESS)
|
|
return;
|
|
|
|
switch (ShutdownCode)
|
|
{
|
|
case WLX_SAS_ACTION_LOGOFF:
|
|
dwValue = WLX_SHUTDOWN_STATE_LOGOFF;
|
|
break;
|
|
|
|
case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF:
|
|
dwValue = WLX_SHUTDOWN_STATE_POWER_OFF;
|
|
break;
|
|
|
|
case WLX_SAS_ACTION_SHUTDOWN_REBOOT:
|
|
dwValue = WLX_SHUTDOWN_STATE_REBOOT;
|
|
break;
|
|
|
|
case WLX_SAS_ACTION_SHUTDOWN_SLEEP:
|
|
dwValue = WLX_SHUTDOWN_STATE_SLEEP;
|
|
break;
|
|
|
|
case WLX_SAS_ACTION_SHUTDOWN_HIBERNATE:
|
|
dwValue = WLX_SHUTDOWN_STATE_HIBERNATE;
|
|
break;
|
|
}
|
|
|
|
RegSetValueExW(hKey,
|
|
L"Shutdown Setting",
|
|
0, REG_DWORD,
|
|
(LPBYTE)&dwValue, sizeof(dwValue));
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
DWORD
|
|
GetAllowedShutdownOptions(
|
|
_In_opt_ HKEY hKeyCurrentUser,
|
|
_In_opt_ HANDLE hUserToken)
|
|
{
|
|
DWORD Options = 0;
|
|
DWORD dwPolicyValue;
|
|
SYSTEM_POWER_CAPABILITIES PowerCaps;
|
|
NTSTATUS Status;
|
|
|
|
dwPolicyValue = 0;
|
|
GetPolicyDWORDValue(hKeyCurrentUser,
|
|
L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer",
|
|
L"NoLogoff", &dwPolicyValue);
|
|
if (dwPolicyValue == 0)
|
|
Options |= WLX_SHUTDOWN_STATE_LOGOFF;
|
|
|
|
#if 0 // TODO: Enable and lookup on a Terminal Services session
|
|
dwPolicyValue = 0;
|
|
GetPolicyDWORDValue(hKeyCurrentUser,
|
|
L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer",
|
|
L"NoDisconnect", &dwPolicyValue);
|
|
if (dwPolicyValue == 0)
|
|
Options |= WLX_SHUTDOWN_STATE_DISCONNECT;
|
|
#endif
|
|
|
|
/* Add shutdown options only if the user has shutdown privilege */
|
|
if (!TestTokenPrivilege(hUserToken, SE_SHUTDOWN_PRIVILEGE))
|
|
return Options; // The user doesn't have them, bail out.
|
|
|
|
/* We can always shutdown and restart */
|
|
Options |= WLX_SHUTDOWN_STATE_POWER_OFF | WLX_SHUTDOWN_STATE_REBOOT;
|
|
|
|
/* Determine whether extra power options are available */
|
|
Status = NtPowerInformation(SystemPowerCapabilities, NULL, 0, &PowerCaps, sizeof(PowerCaps));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("NtPowerInformation(SystemPowerCapabilities) failed (Status 0x%08lx)\n", Status);
|
|
}
|
|
else
|
|
{
|
|
if (IS_PWR_SUSPEND_ALLOWED(&PowerCaps))
|
|
Options |= WLX_SHUTDOWN_STATE_SLEEP;
|
|
|
|
// TODO: "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Power" value "Shutdown"
|
|
// for sleep options. In particular, if set it tells that wakeup events can be disabled.
|
|
// This will enable WLX_SHUTDOWN_STATE_SLEEP2 support.
|
|
|
|
if (IS_PWR_HIBERNATE_ALLOWED(&PowerCaps))
|
|
Options |= WLX_SHUTDOWN_STATE_HIBERNATE;
|
|
}
|
|
|
|
// TODO: Consider Windows 8+ support for:
|
|
// "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Power" value "HiberbootEnabled"
|
|
|
|
// TODO: Add WLX_SHUTDOWN_AUTOUPDATE if:
|
|
// - Policy "Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU" value "NoAUShutdownOption"
|
|
// is absent or zero;
|
|
// - SystemBatteryState detects the computer is on external power;
|
|
// - wuaueng.dll!ordinal_4 tells there are pending updates.
|
|
|
|
return Options;
|
|
}
|
|
|
|
static VOID
|
|
UpdateShutdownDesc(
|
|
_In_ HWND hDlg,
|
|
_In_ PSHUTDOWN_DLG_CONTEXT pContext) // HINSTANCE hInstance
|
|
{
|
|
HWND hwndList = GetDlgItem(hDlg, IDC_SHUTDOWN_ACTION);
|
|
DWORD ShutdownCode;
|
|
UINT DescId = 0;
|
|
WCHAR szBuffer[256];
|
|
|
|
ShutdownCode = SendMessageW(hwndList, CB_GETCURSEL, 0, 0);
|
|
if (ShutdownCode != CB_ERR)
|
|
ShutdownCode = SendMessageW(hwndList, CB_GETITEMDATA, ShutdownCode, 0);
|
|
if (ShutdownCode == CB_ERR) // Invalid selection
|
|
return;
|
|
|
|
switch (ShutdownCode)
|
|
{
|
|
case WLX_SAS_ACTION_LOGOFF:
|
|
DescId = IDS_SHUTDOWN_LOGOFF_DESC;
|
|
break;
|
|
|
|
case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF:
|
|
DescId = IDS_SHUTDOWN_SHUTDOWN_DESC;
|
|
break;
|
|
|
|
case WLX_SAS_ACTION_SHUTDOWN_REBOOT:
|
|
DescId = IDS_SHUTDOWN_RESTART_DESC;
|
|
break;
|
|
|
|
case WLX_SAS_ACTION_SHUTDOWN_SLEEP:
|
|
DescId = IDS_SHUTDOWN_SLEEP_DESC;
|
|
break;
|
|
|
|
case WLX_SAS_ACTION_SHUTDOWN_HIBERNATE:
|
|
DescId = IDS_SHUTDOWN_HIBERNATE_DESC;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
LoadStringW(pContext->pgContext->hDllInstance, DescId, szBuffer, _countof(szBuffer));
|
|
SetDlgItemTextW(hDlg, IDC_SHUTDOWN_DESCRIPTION, szBuffer);
|
|
|
|
if (pContext->bReasonUI)
|
|
{
|
|
BOOL bNotLogoff = (ShutdownCode != WLX_SAS_ACTION_LOGOFF);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_REASON_PLANNED), bNotLogoff);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_REASON_LIST), bNotLogoff);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_REASON_COMMENT), bNotLogoff);
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
ShutdownOnFriendlyInit(
|
|
IN HWND hDlg,
|
|
IN PSHUTDOWN_DLG_CONTEXT pContext)
|
|
{
|
|
PGINA_CONTEXT pgContext = pContext->pgContext;
|
|
HDC hdc;
|
|
LONG lfHeight;
|
|
BOOLEAN bCanSuspend = !!(pContext->ShutdownOptions & (WLX_SHUTDOWN_STATE_SLEEP | WLX_SHUTDOWN_STATE_SLEEP2));
|
|
BOOLEAN bCanHibernate = !!(pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_HIBERNATE);
|
|
|
|
/* Create font for the IDC_TURN_OFF_STATIC static control */
|
|
hdc = GetDC(hDlg);
|
|
lfHeight = -MulDiv(FONT_POINT_SIZE, GetDeviceCaps(hdc, LOGPIXELSY), 72);
|
|
ReleaseDC(hDlg, hdc);
|
|
pContext->hfFont = CreateFontW(lfHeight, 0, 0, 0, FW_MEDIUM, FALSE, 0, 0, 0, 0, 0, 0, 0, L"MS Shell Dlg");
|
|
SendDlgItemMessageW(hDlg, IDC_TURN_OFF_STATIC, WM_SETFONT, (WPARAM)pContext->hfFont, TRUE);
|
|
|
|
/* Create a brush for static controls for fancy shutdown dialog */
|
|
pContext->hBrush = CreateSolidBrush(DARK_GREY_COLOR);
|
|
|
|
/* Gather image strip */
|
|
pContext->hImageStrip = LoadBitmapW(pgContext->hDllInstance, MAKEINTRESOURCEW(IDB_IMAGE_STRIP));
|
|
|
|
/* Set the boolean flags to false */
|
|
pContext->bIsSleepButtonReplaced = FALSE;
|
|
pContext->bTimer = FALSE;
|
|
|
|
EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_HIBERNATE), bCanHibernate);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_SLEEP), bCanSuspend);
|
|
|
|
/* Gather old button function */
|
|
pContext->OldButtonProc = (WNDPROC)GetWindowLongPtrW(GetDlgItem(hDlg, IDC_BUTTON_HIBERNATE), GWLP_WNDPROC);
|
|
|
|
/* Set bIsButtonHot to false, create tooltips for each buttons, make buttons to remember pContext and subclass the buttons */
|
|
for (int i = 0; i < NUMBER_OF_BUTTONS; i++)
|
|
{
|
|
pContext->bIsButtonHot[i] = FALSE;
|
|
SetWindowLongPtrW(GetDlgItem(hDlg, IDC_BUTTON_SHUTDOWN + i),
|
|
GWLP_WNDPROC,
|
|
(LONG_PTR)OwnerDrawButtonSubclass);
|
|
CreateToolTipForButtons(IDC_BUTTON_SHUTDOWN + i,
|
|
IDS_SHUTDOWN_SHUTDOWN_DESC + i,
|
|
hDlg, IDS_SHUTDOWN_SHUTDOWN + i,
|
|
pgContext->hDllInstance);
|
|
}
|
|
|
|
if ((pContext->ShutdownDialogId == IDD_SHUTDOWN_FANCY) && bCanSuspend)
|
|
{
|
|
pContext->iTimer = SetTimer(hDlg, 0, 50, NULL);
|
|
pContext->bTimer = TRUE;
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
ShutdownOnInit(
|
|
IN HWND hDlg,
|
|
IN PSHUTDOWN_DLG_CONTEXT pContext)
|
|
{
|
|
PGINA_CONTEXT pgContext = pContext->pgContext;
|
|
HWND hwndList;
|
|
INT idx, count, i;
|
|
WCHAR szBuffer[256];
|
|
WCHAR szBuffer2[256];
|
|
|
|
if (pContext->bFriendlyUI)
|
|
{
|
|
ShutdownOnFriendlyInit(hDlg, pContext);
|
|
return;
|
|
}
|
|
|
|
hwndList = GetDlgItem(hDlg, IDC_SHUTDOWN_ACTION);
|
|
|
|
/* Clear the content before it's used */
|
|
SendMessageW(hwndList, CB_RESETCONTENT, 0, 0);
|
|
|
|
/* Log off */
|
|
if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_LOGOFF)
|
|
{
|
|
LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_LOGOFF, szBuffer, _countof(szBuffer));
|
|
StringCchPrintfW(szBuffer2, _countof(szBuffer2), szBuffer, pgContext->UserName);
|
|
idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer2);
|
|
if (idx != CB_ERR)
|
|
SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_LOGOFF);
|
|
}
|
|
|
|
/* Shut down */
|
|
if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_POWER_OFF)
|
|
{
|
|
LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_SHUTDOWN, szBuffer, _countof(szBuffer));
|
|
idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer);
|
|
if (idx != CB_ERR)
|
|
SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_POWER_OFF);
|
|
}
|
|
|
|
/* Restart */
|
|
if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_REBOOT)
|
|
{
|
|
LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_RESTART, szBuffer, _countof(szBuffer));
|
|
idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer);
|
|
if (idx != CB_ERR)
|
|
SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_REBOOT);
|
|
}
|
|
|
|
// if (pContext->ShutdownOptions & 0x08) {}
|
|
|
|
/* Sleep */
|
|
if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_SLEEP)
|
|
{
|
|
LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_SLEEP, szBuffer, _countof(szBuffer));
|
|
idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer);
|
|
if (idx != CB_ERR)
|
|
SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_SLEEP);
|
|
}
|
|
|
|
// if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_SLEEP2) {}
|
|
|
|
/* Hibernate */
|
|
if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_HIBERNATE)
|
|
{
|
|
LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_HIBERNATE, szBuffer, _countof(szBuffer));
|
|
idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer);
|
|
if (idx != CB_ERR)
|
|
SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_HIBERNATE);
|
|
}
|
|
|
|
// if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_DISCONNECT) {}
|
|
|
|
// if (pContext->ShutdownOptions & WLX_SHUTDOWN_AUTOUPDATE) {}
|
|
|
|
/* Set the default selection */
|
|
count = SendMessageW(hwndList, CB_GETCOUNT, 0, 0);
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
if (SendMessageW(hwndList, CB_GETITEMDATA, i, 0) == pgContext->nShutdownAction)
|
|
{
|
|
SendMessageW(hwndList, CB_SETCURSEL, i, 0);
|
|
break;
|
|
}
|
|
}
|
|
/* If no item was selected (nShutdownAction with value 0 or invalid,
|
|
* or an action that is currently unavailable), set the selection to
|
|
* the first list item. */
|
|
if (count && (SendMessageW(hwndList, CB_GETCURSEL, 0, 0) == CB_ERR))
|
|
SendMessageW(hwndList, CB_SETCURSEL, 0, 0);
|
|
|
|
/* Update the choice description based on the current selection */
|
|
UpdateShutdownDesc(hDlg, pContext);
|
|
}
|
|
|
|
static VOID
|
|
ShutdownOnOk(
|
|
_In_ HWND hDlg,
|
|
_In_ PGINA_CONTEXT pgContext)
|
|
{
|
|
HWND hwndList = GetDlgItem(hDlg, IDC_SHUTDOWN_ACTION);
|
|
DWORD ShutdownCode = SendMessageW(hwndList, CB_GETCURSEL, 0, 0);
|
|
if (ShutdownCode != CB_ERR)
|
|
{
|
|
ShutdownCode = SendMessageW(hwndList, CB_GETITEMDATA, ShutdownCode, 0);
|
|
if (ShutdownCode != CB_ERR)
|
|
pgContext->nShutdownAction = ShutdownCode;
|
|
}
|
|
}
|
|
|
|
static INT_PTR
|
|
CALLBACK
|
|
ShutdownDialogProc(
|
|
HWND hDlg,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
PSHUTDOWN_DLG_CONTEXT pContext;
|
|
|
|
pContext = (PSHUTDOWN_DLG_CONTEXT)GetWindowLongPtrW(hDlg, GWLP_USERDATA);
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
pContext = (PSHUTDOWN_DLG_CONTEXT)lParam;
|
|
SetWindowLongPtrW(hDlg, GWLP_USERDATA, (LONG_PTR)pContext);
|
|
ShutdownOnInit(hDlg, pContext);
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
if (pContext->bFriendlyUI)
|
|
EndFriendlyDialog(hDlg, pContext);
|
|
return TRUE;
|
|
|
|
case WM_ACTIVATE:
|
|
{
|
|
/*
|
|
* If the user deactivates the shutdown dialog (it loses its focus
|
|
* while the dialog is not being closed), then destroy the dialog
|
|
* and cancel shutdown.
|
|
*/
|
|
if (LOWORD(wParam) == WA_INACTIVE)
|
|
{
|
|
if (!pContext->bCloseDlg)
|
|
{
|
|
pContext->bCloseDlg = TRUE;
|
|
EndDialog(hDlg, IDCANCEL);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
case WM_CLOSE:
|
|
pContext->bCloseDlg = TRUE;
|
|
EndDialog(hDlg, IDCANCEL);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
/* Check for fancy shutdown dialog buttons */
|
|
case IDC_BUTTON_SHUTDOWN: case IDC_BUTTON_REBOOT:
|
|
case IDC_BUTTON_SLEEP: case IDC_BUTTON_HIBERNATE:
|
|
{
|
|
DWORD Button = LOWORD(wParam);
|
|
PGINA_CONTEXT pgContext = pContext->pgContext;
|
|
pgContext->nShutdownAction = 0;
|
|
if (Button == IDC_BUTTON_SHUTDOWN)
|
|
{
|
|
if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_POWER_OFF)
|
|
pgContext->nShutdownAction = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF;
|
|
}
|
|
else if (Button == IDC_BUTTON_REBOOT)
|
|
{
|
|
if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_REBOOT)
|
|
pgContext->nShutdownAction = WLX_SAS_ACTION_SHUTDOWN_REBOOT;
|
|
}
|
|
else if (Button == IDC_BUTTON_SLEEP)
|
|
{
|
|
if (pContext->ShutdownOptions & (WLX_SHUTDOWN_STATE_SLEEP | WLX_SHUTDOWN_STATE_SLEEP2))
|
|
{
|
|
/* Choose "Sleep with wakeup events disabled" if
|
|
* available, otherwise use the regular sleep mode */
|
|
if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_SLEEP2)
|
|
pgContext->nShutdownAction = WLX_SAS_ACTION_SHUTDOWN_SLEEP2;
|
|
else
|
|
pgContext->nShutdownAction = WLX_SAS_ACTION_SHUTDOWN_SLEEP;
|
|
}
|
|
}
|
|
else if (Button == IDC_BUTTON_HIBERNATE)
|
|
{
|
|
if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_HIBERNATE)
|
|
pgContext->nShutdownAction = WLX_SAS_ACTION_SHUTDOWN_HIBERNATE;
|
|
}
|
|
if (pgContext->nShutdownAction == 0)
|
|
break;
|
|
|
|
pContext->bCloseDlg = TRUE;
|
|
EndDialog(hDlg, IDOK);
|
|
break;
|
|
}
|
|
|
|
/* Classic shutdown dialog buttons */
|
|
case IDOK:
|
|
ShutdownOnOk(hDlg, pContext->pgContext);
|
|
|
|
/* Fall back */
|
|
case IDCANCEL:
|
|
case IDHELP:
|
|
pContext->bCloseDlg = TRUE;
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
break;
|
|
|
|
case IDC_SHUTDOWN_ACTION:
|
|
UpdateShutdownDesc(hDlg, pContext);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_CTLCOLORSTATIC:
|
|
{
|
|
/* Either make background transparent or fill it with color for required static controls */
|
|
HDC hdcStatic = (HDC)wParam;
|
|
UINT StaticID = (UINT)GetWindowLongPtrW((HWND)lParam, GWL_ID);
|
|
|
|
switch (StaticID)
|
|
{
|
|
case IDC_TURN_OFF_STATIC:
|
|
SetTextColor(hdcStatic, DARK_GREY_COLOR);
|
|
SetBkMode(hdcStatic, TRANSPARENT);
|
|
return (INT_PTR)GetStockObject(HOLLOW_BRUSH);
|
|
|
|
case IDC_HIBERNATE_STATIC:
|
|
case IDC_SHUTDOWN_STATIC:
|
|
case IDC_SLEEP_STATIC:
|
|
case IDC_RESTART_STATIC:
|
|
SetTextColor(hdcStatic, LIGHT_GREY_COLOR);
|
|
SetBkMode(hdcStatic, TRANSPARENT);
|
|
return (LONG_PTR)pContext->hBrush;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
case WM_DRAWITEM:
|
|
{
|
|
/* Draw bitmaps on required buttons */
|
|
DRAWITEMSTRUCT* pdis = (DRAWITEMSTRUCT*)lParam;
|
|
switch (pdis->CtlID)
|
|
{
|
|
case IDC_BUTTON_SHUTDOWN:
|
|
case IDC_BUTTON_REBOOT:
|
|
case IDC_BUTTON_SLEEP:
|
|
case IDC_BUTTON_HIBERNATE:
|
|
return DrawIconOnOwnerDrawnButtons(pdis, pContext);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_TIMER:
|
|
OnTimer(hDlg, pContext);
|
|
return TRUE;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
INT_PTR
|
|
ShutdownDialog(
|
|
IN HWND hwndDlg,
|
|
IN DWORD ShutdownOptions,
|
|
IN PGINA_CONTEXT pgContext)
|
|
{
|
|
INT_PTR ret;
|
|
SHUTDOWN_DLG_CONTEXT Context = {0};
|
|
|
|
#if 0
|
|
ShutdownOptions = GetAllowedShutdownOptions(hKeyCurrentUser, pgContext->UserToken);
|
|
pgContext->nShutdownAction = LoadShutdownSelState(hKeyCurrentUser);
|
|
ShutdownOptions &= ~dwExcludeOptions;
|
|
#endif
|
|
|
|
/* Bail out early if no shutdown options are available */
|
|
if (ShutdownOptions == 0)
|
|
return IDCANCEL;
|
|
|
|
Context.pgContext = pgContext;
|
|
Context.ShutdownOptions = ShutdownOptions;
|
|
Context.ShutdownDialogId = IDD_SHUTDOWN;
|
|
Context.bCloseDlg = FALSE;
|
|
Context.bReasonUI = GetShutdownReasonUI();
|
|
Context.bFriendlyUI = ShellIsFriendlyUIActive();
|
|
|
|
if (pgContext->hWlx && pgContext->pWlxFuncs && !Context.bFriendlyUI)
|
|
{
|
|
ret = pgContext->pWlxFuncs->WlxDialogBoxParam(pgContext->hWlx,
|
|
pgContext->hDllInstance,
|
|
MAKEINTRESOURCEW(Context.bReasonUI ? IDD_SHUTDOWN_REASON : IDD_SHUTDOWN),
|
|
hwndDlg,
|
|
ShutdownDialogProc,
|
|
(LPARAM)&Context);
|
|
}
|
|
else
|
|
{
|
|
if (Context.bFriendlyUI)
|
|
{
|
|
if (IsShowHibernateButtonActive())
|
|
Context.ShutdownDialogId = IDD_SHUTDOWN_FANCY_LONG;
|
|
else
|
|
Context.ShutdownDialogId = IDD_SHUTDOWN_FANCY;
|
|
}
|
|
|
|
ret = DialogBoxParamW(pgContext->hDllInstance,
|
|
MAKEINTRESOURCEW(Context.bReasonUI ? IDD_SHUTDOWN_REASON : Context.ShutdownDialogId),
|
|
hwndDlg,
|
|
ShutdownDialogProc,
|
|
(LPARAM)&Context);
|
|
}
|
|
|
|
// TODO: Handle shutdown reasons.
|
|
|
|
#if 0
|
|
if (ret == IDOK)
|
|
SaveShutdownSelState(hKeyCurrentUser, pgContext->nShutdownAction);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* NOTES:
|
|
* - Based upon observations on the ShellShutdownDialog() function, the function doesn't actually
|
|
* do anything except show a dialog box and returning a value based upon the value chosen. That
|
|
* means that any code that calls the function has to execute the chosen action (shut down,
|
|
* restart, etc.).
|
|
* - When this function is called in Windows XP, it shows the classic dialog box regardless if
|
|
* SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\LogonType is enabled or not.
|
|
* - When the Help button is pushed, it sends the same return value as IDCANCEL (0x00), but
|
|
* at the same time, it calls the help file directly from the dialog box.
|
|
* - When the dialog is created, it doesn't disable all other input from the other windows.
|
|
* This is done elsewhere. When running the function ShellShutdownDialog() from XP/2K3, if the user
|
|
* clicks out of the window, it automatically closes itself.
|
|
* - The parameter pUserName, never seems to be used when calling the function from Windows XP. Either
|
|
* it was a parameter that was never used in the final version before release, or it has a use that
|
|
* is currently not known.
|
|
*/
|
|
DWORD WINAPI
|
|
ShellShutdownDialog(
|
|
_In_ HWND hWndParent,
|
|
_In_opt_ LPCWSTR pUserName,
|
|
_In_ DWORD dwExcludeOptions)
|
|
{
|
|
INT_PTR dlgValue;
|
|
DWORD ShutdownOptions;
|
|
|
|
/*
|
|
* As we are called by the shell itself, don't use
|
|
* the cached GINA context but use a local copy here.
|
|
*/
|
|
GINA_CONTEXT gContext = {0};
|
|
|
|
ShutdownOptions = GetAllowedShutdownOptions(HKEY_CURRENT_USER, NULL);
|
|
ShutdownOptions &= ~dwExcludeOptions;
|
|
|
|
/* Initialize our local GINA context */
|
|
gContext.hDllInstance = hDllInstance;
|
|
gContext.nShutdownAction = LoadShutdownSelState(HKEY_CURRENT_USER);
|
|
if (gContext.nShutdownAction == 0)
|
|
gContext.nShutdownAction = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF;
|
|
|
|
if (pUserName)
|
|
{
|
|
/* Use the caller-given user name */
|
|
StringCbCopyW(gContext.UserName, sizeof(gContext.UserName), pUserName);
|
|
}
|
|
else
|
|
{
|
|
/* Use either the convenience "Logon User Name" registered by
|
|
* WlxActivateUserShell(), or if not found, retrieve a user name
|
|
* via GetUserNameW() */
|
|
HKEY hKey;
|
|
LONG lRet;
|
|
lRet = RegOpenKeyExW(HKEY_CURRENT_USER,
|
|
L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
|
|
0,
|
|
KEY_QUERY_VALUE, &hKey);
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
DWORD dwType, cbData = sizeof(gContext.UserName);
|
|
lRet = RegQueryValueExW(hKey,
|
|
L"Logon User Name",
|
|
NULL,
|
|
&dwType,
|
|
(PBYTE)gContext.UserName,
|
|
&cbData);
|
|
RegCloseKey(hKey);
|
|
|
|
/* NULL-terminate the string */
|
|
if (lRet != ERROR_SUCCESS)
|
|
cbData = 0;
|
|
gContext.UserName[cbData / sizeof(WCHAR)] = UNICODE_NULL;
|
|
}
|
|
else
|
|
{
|
|
DWORD BufferSize = _countof(gContext.UserName);
|
|
GetUserNameW(gContext.UserName, &BufferSize);
|
|
}
|
|
}
|
|
|
|
/* Load the shutdown dialog box */
|
|
dlgValue = ShutdownDialog(hWndParent, ShutdownOptions, &gContext);
|
|
|
|
/* Determine what to do based on user selection */
|
|
if (dlgValue == IDOK)
|
|
{
|
|
SaveShutdownSelState(HKEY_CURRENT_USER, gContext.nShutdownAction);
|
|
|
|
switch (gContext.nShutdownAction)
|
|
{
|
|
case WLX_SAS_ACTION_LOGOFF:
|
|
return WLX_SHUTDOWN_STATE_LOGOFF;
|
|
|
|
case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF:
|
|
return WLX_SHUTDOWN_STATE_POWER_OFF;
|
|
|
|
case WLX_SAS_ACTION_SHUTDOWN_REBOOT:
|
|
return WLX_SHUTDOWN_STATE_REBOOT;
|
|
|
|
// 0x08
|
|
|
|
case WLX_SAS_ACTION_SHUTDOWN_SLEEP:
|
|
return WLX_SHUTDOWN_STATE_SLEEP;
|
|
|
|
case WLX_SAS_ACTION_SHUTDOWN_SLEEP2:
|
|
return WLX_SHUTDOWN_STATE_SLEEP2;
|
|
|
|
case WLX_SAS_ACTION_SHUTDOWN_HIBERNATE:
|
|
return WLX_SHUTDOWN_STATE_HIBERNATE;
|
|
|
|
// WLX_SHUTDOWN_STATE_DISCONNECT
|
|
}
|
|
}
|
|
/* Help file is called directly here */
|
|
else if (dlgValue == IDHELP)
|
|
{
|
|
FIXME("Help is not implemented yet.\n");
|
|
MessageBoxW(hWndParent, L"Help is not implemented yet.", L"Message", MB_OK | MB_ICONEXCLAMATION);
|
|
}
|
|
else if (dlgValue == -1)
|
|
{
|
|
ERR("Failed to create dialog\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NOTES:
|
|
* - Undocumented, called from MS shell32.dll to show the turn off dialog.
|
|
* - Seems to have the same purpose as ShellShutdownDialog.
|
|
*/
|
|
DWORD WINAPI
|
|
ShellTurnOffDialog(HWND hWnd)
|
|
{
|
|
return ShellShutdownDialog(hWnd, NULL, 0);
|
|
}
|