mirror of
https://github.com/reactos/reactos.git
synced 2026-07-02 17:44:26 +08:00
- Respect system policies for showing or hiding:
* Security dialog "Lock Workstation", "Log Off", "Shut Down",
"Change Password", "Task Manager" buttons;
* "Log Off" entry in the Shutdown dialog;
- Disable the "Shut Down" Security dialog button, and the "Stand by",
"Shut down" entries in the Shutdown dialog, if the logged user
doesn't have the SeShutdownPrivilege.
- Add other missing `WLX_SHUTDOWN_STATE_*` shutdown flags that are
supported by Windows.
- Improve the retrieval of shutdown options to be displayed in the
"Shut Down" dialog. In particular, don't hardcode any sort of
defaults, but let them come from what the user can do (Is logoff
allowed? Does (s)he have the rights to shutdown/reboot? etc.).
If no shutdown options are available, then simply don't display
the dialog!
- Don't hardcode the default selected shutdown option. Either it comes
from the user's registry and is valid (i.e. corresponds to an existing
shutdown option in the dialog), otherwise, fall back to the first
option in the dialog.
In particular this means:
* when opening the "Shut Down" dialog from the C-A-D Security
dialog, by default we will select the "Log Off" option, or
whatever the user last selected;
* when opening the dialog from the "Turn Off" Start Menu item, default
to the "Shut down" option, or whatever the user last selected.
* when opening the dialog from the C-A-D "Log On" dialog (no user
is logged in), the "Shut down" option will also be selected, or
whatever the system operator last selected.
- For the shell-invokable `ShellShutdownDialog()` function, implement
support for using a custom display user name, or the one in the
`"Logon User Name"` registry value saved by `WlxActivateUserShell()`.
Plus, the 3rd parameter specifies the list of shutdown options to
*exclude* from the options list.
1690 lines
55 KiB
C++
1690 lines
55 KiB
C++
/*
|
|
* common shell dialogs
|
|
*
|
|
* Copyright 2000 Juergen Schmied
|
|
* Copyright 2018 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
|
|
* Copyright 2021 Arnav Bhatt <arnavbhatt288@gmail.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
typedef struct
|
|
{
|
|
HWND hwndOwner;
|
|
HICON hIcon;
|
|
LPCWSTR lpstrDirectory;
|
|
LPCWSTR lpstrTitle;
|
|
LPCWSTR lpstrDescription;
|
|
UINT uFlags;
|
|
BOOL bCoInited;
|
|
} RUNFILEDLGPARAMS;
|
|
|
|
typedef struct
|
|
{
|
|
BOOL bFriendlyUI;
|
|
BOOL bIsButtonHot[2];
|
|
HBITMAP hImageStrip;
|
|
HBRUSH hBrush;
|
|
HFONT hfFont;
|
|
BOOL bCloseDlg;
|
|
WNDPROC OldButtonProc;
|
|
} LOGOFF_DLG_CONTEXT, *PLOGOFF_DLG_CONTEXT;
|
|
|
|
typedef BOOL (WINAPI * LPFNOFN) (OPENFILENAMEW *);
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(shell);
|
|
static INT_PTR CALLBACK RunDlgProc(HWND, UINT, WPARAM, LPARAM);
|
|
static void FillList(HWND, LPWSTR, UINT, BOOL);
|
|
|
|
|
|
/*************************************************************************
|
|
* PickIconDlg [SHELL32.62]
|
|
*
|
|
*/
|
|
|
|
typedef struct
|
|
{
|
|
HMODULE hLibrary;
|
|
HWND hDlgCtrl;
|
|
WCHAR szPath[MAX_PATH];
|
|
INT Index;
|
|
INT nIcons;
|
|
HICON *phIcons;
|
|
} PICK_ICON_CONTEXT, *PPICK_ICON_CONTEXT;
|
|
|
|
BOOL CALLBACK EnumPickIconResourceProc(HMODULE hModule,
|
|
LPCWSTR lpszType,
|
|
LPWSTR lpszName,
|
|
LONG_PTR lParam)
|
|
{
|
|
PPICK_ICON_CONTEXT pIconContext = PPICK_ICON_CONTEXT(lParam);
|
|
HWND hDlgCtrl = pIconContext->hDlgCtrl;
|
|
|
|
if (IS_INTRESOURCE(lpszName))
|
|
lParam = LOWORD(lpszName);
|
|
else
|
|
lParam = -1;
|
|
|
|
SendMessageW(hDlgCtrl, LB_ADDSTRING, 0, lParam);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
DestroyIconList(HWND hDlgCtrl, PPICK_ICON_CONTEXT pIconContext)
|
|
{
|
|
int count;
|
|
int index;
|
|
|
|
count = SendMessageW(hDlgCtrl, LB_GETCOUNT, 0, 0);
|
|
if (count == LB_ERR)
|
|
return;
|
|
|
|
for(index = 0; index < count; index++)
|
|
{
|
|
DestroyIcon(pIconContext->phIcons[index]);
|
|
pIconContext->phIcons[index] = NULL;
|
|
}
|
|
}
|
|
|
|
static BOOL
|
|
DoLoadIcons(HWND hwndDlg, PPICK_ICON_CONTEXT pIconContext, LPCWSTR pszFile)
|
|
{
|
|
WCHAR szExpandedPath[MAX_PATH];
|
|
|
|
// Destroy previous icons
|
|
DestroyIconList(pIconContext->hDlgCtrl, pIconContext);
|
|
SendMessageW(pIconContext->hDlgCtrl, LB_RESETCONTENT, 0, 0);
|
|
delete[] pIconContext->phIcons;
|
|
|
|
// Store the path
|
|
StringCchCopyW(pIconContext->szPath, _countof(pIconContext->szPath), pszFile);
|
|
ExpandEnvironmentStringsW(pszFile, szExpandedPath, _countof(szExpandedPath));
|
|
|
|
// Load the module if possible
|
|
HMODULE hLibrary = LoadLibraryExW(szExpandedPath, NULL, LOAD_LIBRARY_AS_DATAFILE);
|
|
if (pIconContext->hLibrary)
|
|
FreeLibrary(pIconContext->hLibrary);
|
|
pIconContext->hLibrary = hLibrary;
|
|
|
|
if (pIconContext->hLibrary)
|
|
{
|
|
// Load the icons from the module
|
|
pIconContext->nIcons = ExtractIconExW(szExpandedPath, -1, NULL, NULL, 0);
|
|
pIconContext->phIcons = new HICON[pIconContext->nIcons];
|
|
|
|
if (ExtractIconExW(szExpandedPath, 0, pIconContext->phIcons, NULL, pIconContext->nIcons))
|
|
{
|
|
EnumResourceNamesW(pIconContext->hLibrary, RT_GROUP_ICON, EnumPickIconResourceProc, (LPARAM)pIconContext);
|
|
}
|
|
else
|
|
{
|
|
pIconContext->nIcons = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// .ico file
|
|
pIconContext->nIcons = 1;
|
|
pIconContext->phIcons = new HICON[1];
|
|
|
|
if (ExtractIconExW(szExpandedPath, 0, pIconContext->phIcons, NULL, pIconContext->nIcons))
|
|
{
|
|
SendMessageW(pIconContext->hDlgCtrl, LB_ADDSTRING, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
pIconContext->nIcons = 0;
|
|
}
|
|
}
|
|
|
|
SetDlgItemTextW(hwndDlg, IDC_EDIT_PATH, pIconContext->szPath);
|
|
SendMessageW(pIconContext->hDlgCtrl, LB_SETCURSEL, 0, 0);
|
|
|
|
if (pIconContext->nIcons == 0)
|
|
{
|
|
delete[] pIconContext->phIcons;
|
|
pIconContext->phIcons = NULL;
|
|
}
|
|
|
|
return (pIconContext->nIcons > 0);
|
|
}
|
|
|
|
static void NoIconsInFile(HWND hwndDlg, PPICK_ICON_CONTEXT pIconContext)
|
|
{
|
|
// Show an error message
|
|
CStringW strText, strTitle(MAKEINTRESOURCEW(IDS_PICK_ICON_TITLE));
|
|
strText.Format(IDS_NO_ICONS, pIconContext->szPath);
|
|
MessageBoxW(hwndDlg, strText, strTitle, MB_ICONWARNING);
|
|
|
|
// Load the default icons
|
|
DoLoadIcons(hwndDlg, pIconContext, g_pszShell32);
|
|
}
|
|
|
|
// Icon size
|
|
#define CX_ICON GetSystemMetrics(SM_CXICON)
|
|
#define CY_ICON GetSystemMetrics(SM_CYICON)
|
|
|
|
// Item size
|
|
#define CX_ITEM (CX_ICON + 4)
|
|
#define CY_ITEM (CY_ICON + 12)
|
|
|
|
INT_PTR CALLBACK PickIconProc(
|
|
HWND hwndDlg,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
LPMEASUREITEMSTRUCT lpmis;
|
|
LPDRAWITEMSTRUCT lpdis;
|
|
HICON hIcon;
|
|
INT index, count;
|
|
WCHAR szText[MAX_PATH], szFilter[100];
|
|
OPENFILENAMEW ofn;
|
|
PPICK_ICON_CONTEXT pIconContext = (PPICK_ICON_CONTEXT)GetWindowLongPtr(hwndDlg, DWLP_USER);
|
|
|
|
switch(uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
pIconContext = (PPICK_ICON_CONTEXT)lParam;
|
|
SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pIconContext);
|
|
pIconContext->hDlgCtrl = GetDlgItem(hwndDlg, IDC_PICKICON_LIST);
|
|
|
|
SendMessageW(pIconContext->hDlgCtrl, LB_SETCOLUMNWIDTH, CX_ITEM, 0);
|
|
|
|
// Load the icons
|
|
if (!DoLoadIcons(hwndDlg, pIconContext, pIconContext->szPath))
|
|
NoIconsInFile(hwndDlg, pIconContext);
|
|
|
|
// Set the selection
|
|
count = SendMessageW(pIconContext->hDlgCtrl, LB_GETCOUNT, 0, 0);
|
|
if (count != LB_ERR)
|
|
{
|
|
if (pIconContext->Index < 0)
|
|
{
|
|
// A negative value will be interpreted as a negated resource ID.
|
|
LPARAM lParam = -pIconContext->Index;
|
|
pIconContext->Index = (INT)SendMessageW(pIconContext->hDlgCtrl, LB_FINDSTRINGEXACT, -1, lParam);
|
|
}
|
|
|
|
if (pIconContext->Index < 0 || count <= pIconContext->Index)
|
|
pIconContext->Index = 0;
|
|
|
|
SendMessageW(pIconContext->hDlgCtrl, LB_SETCURSEL, pIconContext->Index, 0);
|
|
SendMessageW(pIconContext->hDlgCtrl, LB_SETTOPINDEX, pIconContext->Index, 0);
|
|
}
|
|
|
|
SHAutoComplete(GetDlgItem(hwndDlg, IDC_EDIT_PATH), SHACF_DEFAULT);
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
DestroyIconList(pIconContext->hDlgCtrl, pIconContext);
|
|
delete[] pIconContext->phIcons;
|
|
|
|
if (pIconContext->hLibrary)
|
|
FreeLibrary(pIconContext->hLibrary);
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDOK:
|
|
{
|
|
/* Check whether the path edit control has been modified; if so load the icons instead of validating */
|
|
GetDlgItemTextW(hwndDlg, IDC_EDIT_PATH, szText, _countof(szText));
|
|
if (lstrcmpiW(szText, pIconContext->szPath))
|
|
{
|
|
if (!DoLoadIcons(hwndDlg, pIconContext, szText))
|
|
NoIconsInFile(hwndDlg, pIconContext);
|
|
break;
|
|
}
|
|
|
|
/* The path edit control has not been modified, return the selection */
|
|
pIconContext->Index = (INT)SendMessageW(pIconContext->hDlgCtrl, LB_GETCURSEL, 0, 0);
|
|
GetDlgItemTextW(hwndDlg, IDC_EDIT_PATH, pIconContext->szPath, _countof(pIconContext->szPath));
|
|
EndDialog(hwndDlg, 1);
|
|
break;
|
|
}
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hwndDlg, 0);
|
|
break;
|
|
|
|
case IDC_PICKICON_LIST:
|
|
switch (HIWORD(wParam))
|
|
{
|
|
case LBN_SELCHANGE:
|
|
InvalidateRect((HWND)lParam, NULL, TRUE);
|
|
break;
|
|
|
|
case LBN_DBLCLK:
|
|
SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDOK, 0), 0);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case IDC_BUTTON_PATH:
|
|
{
|
|
// Choose the module path
|
|
CStringW strTitle;
|
|
szText[0] = 0;
|
|
szFilter[0] = 0;
|
|
ZeroMemory(&ofn, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = hwndDlg;
|
|
ofn.lpstrFile = szText;
|
|
ofn.nMaxFile = _countof(szText);
|
|
strTitle.LoadString(IDS_PICK_ICON_TITLE);
|
|
ofn.lpstrTitle = strTitle;
|
|
LoadStringW(shell32_hInstance, IDS_PICK_ICON_FILTER, szFilter, _countof(szFilter));
|
|
ofn.lpstrFilter = szFilter;
|
|
if (!GetOpenFileNameW(&ofn))
|
|
break;
|
|
|
|
// Load the icons
|
|
if (!DoLoadIcons(hwndDlg, pIconContext, szText))
|
|
NoIconsInFile(hwndDlg, pIconContext);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_MEASUREITEM:
|
|
lpmis = (LPMEASUREITEMSTRUCT)lParam;
|
|
lpmis->itemHeight = CY_ITEM;
|
|
return TRUE;
|
|
|
|
case WM_DRAWITEM:
|
|
{
|
|
lpdis = (LPDRAWITEMSTRUCT)lParam;
|
|
if (lpdis->itemID == (UINT)-1)
|
|
break;
|
|
switch (lpdis->itemAction) // FIXME: MSDN says that more than one of these can be set
|
|
{
|
|
// FIXME: ODA_FOCUS
|
|
case ODA_SELECT:
|
|
case ODA_DRAWENTIRE:
|
|
{
|
|
index = SendMessageW(pIconContext->hDlgCtrl, LB_GETCURSEL, 0, 0);
|
|
hIcon = pIconContext->phIcons[lpdis->itemID];
|
|
|
|
if (lpdis->itemID == (UINT)index)
|
|
FillRect(lpdis->hDC, &lpdis->rcItem, (HBRUSH)(COLOR_HIGHLIGHT + 1));
|
|
else
|
|
FillRect(lpdis->hDC, &lpdis->rcItem, (HBRUSH)(COLOR_WINDOW + 1));
|
|
|
|
// Centering
|
|
INT x = lpdis->rcItem.left + (CX_ITEM - CX_ICON) / 2;
|
|
INT y = lpdis->rcItem.top + (CY_ITEM - CY_ICON) / 2;
|
|
|
|
DrawIconEx(lpdis->hDC, x, y, hIcon, 0, 0, 0, NULL, DI_NORMAL);
|
|
break;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL WINAPI PickIconDlg(
|
|
HWND hWndOwner,
|
|
LPWSTR lpstrFile,
|
|
UINT nMaxFile,
|
|
INT* lpdwIconIndex)
|
|
{
|
|
CCoInit ComInit; // For SHAutoComplete (CORE-20030)
|
|
int res;
|
|
WCHAR szExpandedPath[MAX_PATH];
|
|
|
|
// Initialize the dialog
|
|
PICK_ICON_CONTEXT IconContext = {0};
|
|
IconContext.Index = *lpdwIconIndex;
|
|
StringCchCopyW(IconContext.szPath, _countof(IconContext.szPath), lpstrFile);
|
|
ExpandEnvironmentStringsW(lpstrFile, szExpandedPath, _countof(szExpandedPath));
|
|
|
|
if (!szExpandedPath[0] ||
|
|
GetFileAttributesW(szExpandedPath) == INVALID_FILE_ATTRIBUTES)
|
|
{
|
|
if (szExpandedPath[0])
|
|
{
|
|
// No such file
|
|
CStringW strText, strTitle(MAKEINTRESOURCEW(IDS_PICK_ICON_TITLE));
|
|
strText.Format(IDS_FILE_NOT_FOUND, lpstrFile);
|
|
MessageBoxW(hWndOwner, strText, strTitle, MB_ICONWARNING);
|
|
}
|
|
|
|
// Set the default value
|
|
StringCchCopyW(IconContext.szPath, _countof(IconContext.szPath), g_pszShell32);
|
|
}
|
|
|
|
// Show the dialog
|
|
res = DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_PICK_ICON), hWndOwner, PickIconProc, (LPARAM)&IconContext);
|
|
if (res)
|
|
{
|
|
// Store the selected icon
|
|
StringCchCopyW(lpstrFile, nMaxFile, IconContext.szPath);
|
|
*lpdwIconIndex = IconContext.Index;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* RunFileDlg [internal]
|
|
*
|
|
* The Unicode function that is available as ordinal 61 on Windows NT/2000/XP/...
|
|
*/
|
|
void WINAPI RunFileDlg(
|
|
HWND hWndOwner,
|
|
HICON hIcon,
|
|
LPCWSTR lpstrDirectory,
|
|
LPCWSTR lpstrTitle,
|
|
LPCWSTR lpstrDescription,
|
|
UINT uFlags)
|
|
{
|
|
TRACE("\n");
|
|
|
|
RUNFILEDLGPARAMS rfdp;
|
|
rfdp.hwndOwner = hWndOwner;
|
|
rfdp.hIcon = hIcon;
|
|
rfdp.lpstrDirectory = lpstrDirectory;
|
|
rfdp.lpstrTitle = lpstrTitle;
|
|
rfdp.lpstrDescription = lpstrDescription;
|
|
rfdp.uFlags = uFlags;
|
|
|
|
DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_RUN), hWndOwner, RunDlgProc, (LPARAM)&rfdp);
|
|
}
|
|
|
|
|
|
/* find the directory that contains the file being run */
|
|
static LPWSTR RunDlg_GetParentDir(LPCWSTR cmdline)
|
|
{
|
|
const WCHAR *src;
|
|
WCHAR *dest, *result, *result_end=NULL;
|
|
|
|
result = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(strlenW(cmdline)+5));
|
|
|
|
if (NULL == result)
|
|
{
|
|
TRACE("HeapAlloc couldn't allocate %d bytes\n", sizeof(WCHAR)*(strlenW(cmdline)+5));
|
|
return NULL;
|
|
}
|
|
|
|
src = cmdline;
|
|
dest = result;
|
|
|
|
if (*src == '"')
|
|
{
|
|
src++;
|
|
while (*src && *src != '"')
|
|
{
|
|
if (*src == '\\')
|
|
result_end = dest;
|
|
*dest++ = *src++;
|
|
}
|
|
}
|
|
else {
|
|
while (*src)
|
|
{
|
|
if (isspaceW(*src))
|
|
{
|
|
*dest = 0;
|
|
if (INVALID_FILE_ATTRIBUTES != GetFileAttributesW(result))
|
|
break;
|
|
strcatW(dest, L".exe");
|
|
if (INVALID_FILE_ATTRIBUTES != GetFileAttributesW(result))
|
|
break;
|
|
}
|
|
else if (*src == '\\')
|
|
result_end = dest;
|
|
*dest++ = *src++;
|
|
}
|
|
}
|
|
|
|
if (result_end)
|
|
{
|
|
*result_end = 0;
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, result);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static void EnableOkButtonFromEditContents(HWND hwnd)
|
|
{
|
|
BOOL Enable = FALSE;
|
|
INT Length, n;
|
|
HWND Edit = GetDlgItem(hwnd, IDC_RUNDLG_EDITPATH);
|
|
Length = GetWindowTextLengthW(Edit);
|
|
if (Length > 0)
|
|
{
|
|
PWCHAR psz = (PWCHAR)HeapAlloc(GetProcessHeap(), 0, (Length + 1) * sizeof(WCHAR));
|
|
if (psz)
|
|
{
|
|
GetWindowTextW(Edit, psz, Length + 1);
|
|
for (n = 0; n < Length && !Enable; ++n)
|
|
Enable = psz[n] != ' ';
|
|
HeapFree(GetProcessHeap(), 0, psz);
|
|
}
|
|
else
|
|
Enable = TRUE;
|
|
}
|
|
EnableWindow(GetDlgItem(hwnd, IDOK), Enable);
|
|
}
|
|
|
|
/* Dialog procedure for RunFileDlg */
|
|
static INT_PTR CALLBACK RunDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
RUNFILEDLGPARAMS *prfdp = (RUNFILEDLGPARAMS *)GetWindowLongPtrW(hwnd, DWLP_USER);
|
|
HWND hwndCombo, hwndEdit;
|
|
COMBOBOXINFO ComboInfo;
|
|
|
|
switch (message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
prfdp = (RUNFILEDLGPARAMS *)lParam;
|
|
SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR)prfdp);
|
|
|
|
if (prfdp->lpstrTitle)
|
|
SetWindowTextW(hwnd, prfdp->lpstrTitle);
|
|
if (prfdp->lpstrDescription)
|
|
SetWindowTextW(GetDlgItem(hwnd, IDC_RUNDLG_DESCRIPTION), prfdp->lpstrDescription);
|
|
if (prfdp->uFlags & RFF_NOBROWSE)
|
|
{
|
|
HWND browse = GetDlgItem(hwnd, IDC_RUNDLG_BROWSE);
|
|
ShowWindow(browse, SW_HIDE);
|
|
EnableWindow(browse, FALSE);
|
|
}
|
|
if (prfdp->uFlags & RFF_NOLABEL)
|
|
ShowWindow(GetDlgItem(hwnd, IDC_RUNDLG_LABEL), SW_HIDE);
|
|
if (prfdp->uFlags & RFF_NOSEPARATEMEM)
|
|
{
|
|
FIXME("RFF_NOSEPARATEMEM not supported\n");
|
|
}
|
|
|
|
/* Use the default Shell Run icon if no one is specified */
|
|
if (prfdp->hIcon == NULL)
|
|
prfdp->hIcon = LoadIconW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_RUN));
|
|
/*
|
|
* NOTE: Starting Windows Vista, the "Run File" dialog gets a
|
|
* title icon that remains the same as the default one, even if
|
|
* the user specifies a custom icon.
|
|
* Since we currently imitate Windows 2003, therefore do not show
|
|
* any title icon.
|
|
*/
|
|
// SendMessageW(hwnd, WM_SETICON, ICON_BIG, (LPARAM)prfdp->hIcon);
|
|
// SendMessageW(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)prfdp->hIcon);
|
|
SendMessageW(GetDlgItem(hwnd, IDC_RUNDLG_ICON), STM_SETICON, (WPARAM)prfdp->hIcon, 0);
|
|
|
|
hwndCombo = GetDlgItem(hwnd, IDC_RUNDLG_EDITPATH);
|
|
FillList(hwndCombo, NULL, 0, (prfdp->uFlags & RFF_NODEFAULT) == 0);
|
|
EnableOkButtonFromEditContents(hwnd);
|
|
|
|
ComboInfo.cbSize = sizeof(ComboInfo);
|
|
GetComboBoxInfo(hwndCombo, &ComboInfo);
|
|
hwndEdit = ComboInfo.hwndItem;
|
|
ASSERT(::IsWindow(hwndEdit));
|
|
|
|
// SHAutoComplete needs co init
|
|
prfdp->bCoInited = SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
|
|
|
|
SHAutoComplete(hwndEdit, SHACF_FILESYSTEM | SHACF_FILESYS_ONLY | SHACF_URLALL);
|
|
|
|
SetFocus(hwndCombo);
|
|
return TRUE;
|
|
|
|
case WM_DESTROY:
|
|
if (prfdp->bCoInited)
|
|
CoUninitialize();
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDOK:
|
|
{
|
|
LRESULT lRet;
|
|
HWND htxt = GetDlgItem(hwnd, IDC_RUNDLG_EDITPATH);
|
|
INT ic;
|
|
WCHAR *psz, *pszExpanded, *parent = NULL;
|
|
DWORD cchExpand;
|
|
SHELLEXECUTEINFOW sei = { sizeof(sei) };
|
|
NMRUNFILEDLGW nmrfd;
|
|
|
|
ic = GetWindowTextLengthW(htxt);
|
|
if (ic == 0)
|
|
{
|
|
EndDialog(hwnd, IDCANCEL);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Allocate a new MRU entry, we need to add two characters
|
|
* for the terminating "\\1" part, then the NULL character.
|
|
*/
|
|
psz = (WCHAR*)HeapAlloc(GetProcessHeap(), 0, (ic + 2 + 1)*sizeof(WCHAR));
|
|
if (!psz)
|
|
{
|
|
EndDialog(hwnd, IDCANCEL);
|
|
return TRUE;
|
|
}
|
|
|
|
GetWindowTextW(htxt, psz, ic + 1);
|
|
sei.hwnd = hwnd;
|
|
sei.nShow = SW_SHOWNORMAL;
|
|
sei.lpFile = psz;
|
|
StrTrimW(psz, L" \t");
|
|
|
|
if (wcschr(psz, L'%') != NULL)
|
|
{
|
|
cchExpand = ExpandEnvironmentStringsW(psz, NULL, 0);
|
|
pszExpanded = (WCHAR*)HeapAlloc(GetProcessHeap(), 0, cchExpand * sizeof(WCHAR));
|
|
if (!pszExpanded)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, psz);
|
|
EndDialog(hwnd, IDCANCEL);
|
|
return TRUE;
|
|
}
|
|
ExpandEnvironmentStringsW(psz, pszExpanded, cchExpand);
|
|
StrTrimW(pszExpanded, L" \t");
|
|
}
|
|
else
|
|
{
|
|
pszExpanded = psz;
|
|
}
|
|
|
|
/*
|
|
* The precedence is the following: first the user-given
|
|
* current directory is used; if there is none, a current
|
|
* directory is computed if the RFF_CALCDIRECTORY is set,
|
|
* otherwise no current directory is defined.
|
|
*/
|
|
LPCWSTR pszStartDir;
|
|
if (prfdp->lpstrDirectory)
|
|
{
|
|
sei.lpDirectory = prfdp->lpstrDirectory;
|
|
pszStartDir = prfdp->lpstrDirectory;
|
|
}
|
|
else if (prfdp->uFlags & RFF_CALCDIRECTORY)
|
|
{
|
|
sei.lpDirectory = parent = RunDlg_GetParentDir(sei.lpFile);
|
|
pszStartDir = parent = RunDlg_GetParentDir(pszExpanded);
|
|
}
|
|
else
|
|
{
|
|
sei.lpDirectory = NULL;
|
|
pszStartDir = NULL;
|
|
}
|
|
|
|
/* Hide the dialog for now on, we will show it up in case of retry */
|
|
ShowWindow(hwnd, SW_HIDE);
|
|
|
|
/*
|
|
* As shown by manual tests on Windows, modifying the contents
|
|
* of the notification structure will not modify what the
|
|
* Run-Dialog will use for the nShow parameter. However the
|
|
* lpFile and lpDirectory pointers are set to the buffers used
|
|
* by the Run-Dialog, as a consequence they can be modified by
|
|
* the notification receiver, as long as it respects the lengths
|
|
* of the buffers (to avoid buffer overflows).
|
|
*/
|
|
nmrfd.hdr.code = RFN_VALIDATE;
|
|
nmrfd.hdr.hwndFrom = hwnd;
|
|
nmrfd.hdr.idFrom = 0;
|
|
nmrfd.lpFile = pszExpanded;
|
|
nmrfd.lpDirectory = pszStartDir;
|
|
nmrfd.nShow = SW_SHOWNORMAL;
|
|
|
|
lRet = SendMessageW(prfdp->hwndOwner, WM_NOTIFY, 0, (LPARAM)&nmrfd.hdr);
|
|
|
|
switch (lRet)
|
|
{
|
|
case RF_CANCEL:
|
|
EndDialog(hwnd, IDCANCEL);
|
|
break;
|
|
|
|
case RF_OK:
|
|
/* We use SECL_NO_UI because we don't want to see
|
|
* errors here, but we will try again below and
|
|
* there we will output our errors. */
|
|
if (SUCCEEDED(ShellExecCmdLine(hwnd, pszExpanded, pszStartDir, SW_SHOWNORMAL, NULL,
|
|
SECL_ALLOW_NONEXE | SECL_NO_UI)))
|
|
{
|
|
/* Call GetWindowText again in case the contents of the edit box have changed. */
|
|
GetWindowTextW(htxt, psz, ic + 1);
|
|
FillList(htxt, psz, ic + 2 + 1, FALSE);
|
|
EndDialog(hwnd, IDOK);
|
|
break;
|
|
}
|
|
else if (ShellExecuteExW(&sei))
|
|
{
|
|
/* Call GetWindowText again in case the contents of the edit box have changed. */
|
|
GetWindowTextW(htxt, psz, ic + 1);
|
|
FillList(htxt, psz, ic + 2 + 1, FALSE);
|
|
EndDialog(hwnd, IDOK);
|
|
break;
|
|
}
|
|
|
|
/* Fall-back */
|
|
case RF_RETRY:
|
|
default:
|
|
SendMessageW(htxt, CB_SETEDITSEL, 0, MAKELPARAM (0, -1));
|
|
/* Show back the dialog */
|
|
ShowWindow(hwnd, SW_SHOW);
|
|
break;
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, parent);
|
|
HeapFree(GetProcessHeap(), 0, psz);
|
|
if (psz != pszExpanded)
|
|
HeapFree(GetProcessHeap(), 0, pszExpanded);
|
|
return TRUE;
|
|
}
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hwnd, IDCANCEL);
|
|
return TRUE;
|
|
|
|
case IDC_RUNDLG_BROWSE:
|
|
{
|
|
HMODULE hComdlg = NULL;
|
|
LPFNOFN ofnProc = NULL;
|
|
WCHAR szFName[1024] = {0};
|
|
WCHAR filter[MAX_PATH], szCaption[MAX_PATH];
|
|
OPENFILENAMEW ofn;
|
|
|
|
LoadStringW(shell32_hInstance, IDS_RUNDLG_BROWSE_FILTER, filter, _countof(filter));
|
|
LoadStringW(shell32_hInstance, IDS_RUNDLG_BROWSE_CAPTION, szCaption, _countof(szCaption));
|
|
|
|
ZeroMemory(&ofn, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = hwnd;
|
|
ofn.lpstrFilter = filter;
|
|
ofn.lpstrFile = szFName;
|
|
ofn.nMaxFile = _countof(szFName) - 1;
|
|
ofn.lpstrTitle = szCaption;
|
|
ofn.Flags = OFN_ENABLESIZING | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_EXPLORER;
|
|
ofn.lpstrInitialDir = prfdp->lpstrDirectory;
|
|
|
|
if (NULL == (hComdlg = LoadLibraryExW(L"comdlg32", NULL, 0)) ||
|
|
NULL == (ofnProc = (LPFNOFN)GetProcAddress(hComdlg, "GetOpenFileNameW")))
|
|
{
|
|
ERR("Couldn't get GetOpenFileName function entry (lib=%p, proc=%p)\n", hComdlg, ofnProc);
|
|
ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_RUNDLG_BROWSE_ERROR), NULL, MB_OK | MB_ICONERROR);
|
|
return TRUE;
|
|
}
|
|
|
|
if (ofnProc(&ofn))
|
|
{
|
|
SetFocus(GetDlgItem(hwnd, IDOK));
|
|
SetWindowTextW(GetDlgItem(hwnd, IDC_RUNDLG_EDITPATH), szFName);
|
|
SendMessageW(GetDlgItem(hwnd, IDC_RUNDLG_EDITPATH), CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
|
|
EnableOkButtonFromEditContents(hwnd);
|
|
SetFocus(GetDlgItem(hwnd, IDOK));
|
|
}
|
|
|
|
FreeLibrary(hComdlg);
|
|
|
|
return TRUE;
|
|
}
|
|
case IDC_RUNDLG_EDITPATH:
|
|
{
|
|
if (HIWORD(wParam) == CBN_EDITCHANGE)
|
|
EnableOkButtonFromEditContents(hwnd);
|
|
|
|
// Delay handling dropdown changes until the edit box has been updated.
|
|
if (HIWORD(wParam) == CBN_SELCHANGE)
|
|
PostMessage(hwnd, message, MAKELONG(IDC_RUNDLG_EDITPATH, CBN_EDITCHANGE), lParam);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* This function grabs the MRU list from the registry and fills the combo-list
|
|
* for the "Run" dialog above. fShowDefault is ignored if pszLatest != NULL.
|
|
*/
|
|
// FIXME: Part of this code should be part of some MRUList API,
|
|
// that is scattered amongst shell32, comctl32 (?!) and comdlg32.
|
|
static void FillList(HWND hCb, LPWSTR pszLatest, UINT cchStr, BOOL fShowDefault)
|
|
{
|
|
HKEY hkey;
|
|
WCHAR *pszList = NULL, *pszCmd = NULL, *pszTmp = NULL, cMatch = 0, cMax = 0x60;
|
|
WCHAR szIndex[2] = L"-";
|
|
UINT cchLatest;
|
|
DWORD dwType, icList = 0, icCmd = 0;
|
|
LRESULT lRet;
|
|
UINT Nix;
|
|
|
|
/*
|
|
* Retrieve the string length of pszLatest and check whether its buffer size
|
|
* (cchStr in number of characters) is large enough to add the terminating "\\1"
|
|
* (and the NULL character).
|
|
*/
|
|
if (pszLatest)
|
|
{
|
|
cchLatest = wcslen(pszLatest);
|
|
if (cchStr < cchLatest + 2 + 1)
|
|
{
|
|
TRACE("pszLatest buffer is not large enough (%d) to hold the MRU terminator.\n", cchStr);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cchStr = 0;
|
|
}
|
|
|
|
SendMessageW(hCb, CB_RESETCONTENT, 0, 0);
|
|
|
|
lRet = RegCreateKeyExW(HKEY_CURRENT_USER,
|
|
L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU",
|
|
0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL);
|
|
if (lRet != ERROR_SUCCESS)
|
|
{
|
|
TRACE("Unable to open or create the RunMRU key, error %d\n", GetLastError());
|
|
return;
|
|
}
|
|
|
|
lRet = RegQueryValueExW(hkey, L"MRUList", NULL, &dwType, NULL, &icList);
|
|
if (lRet == ERROR_SUCCESS && dwType == REG_SZ && icList > sizeof(WCHAR))
|
|
{
|
|
pszList = (WCHAR*)HeapAlloc(GetProcessHeap(), 0, icList);
|
|
if (!pszList)
|
|
{
|
|
TRACE("HeapAlloc failed to allocate %d bytes\n", icList);
|
|
goto Continue;
|
|
}
|
|
pszList[0] = L'\0';
|
|
|
|
lRet = RegQueryValueExW(hkey, L"MRUList", NULL, NULL, (LPBYTE)pszList, &icList);
|
|
if (lRet != ERROR_SUCCESS)
|
|
{
|
|
TRACE("Unable to grab MRUList, error %d\n", GetLastError());
|
|
pszList[0] = L'\0';
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Continue:
|
|
icList = sizeof(WCHAR);
|
|
pszList = (WCHAR*)HeapAlloc(GetProcessHeap(), 0, icList);
|
|
if (!pszList)
|
|
{
|
|
TRACE("HeapAlloc failed to allocate %d bytes\n", icList);
|
|
RegCloseKey(hkey);
|
|
return;
|
|
}
|
|
pszList[0] = L'\0';
|
|
}
|
|
|
|
/* Convert the number of bytes from MRUList into number of characters (== number of indices) */
|
|
icList /= sizeof(WCHAR);
|
|
|
|
for (Nix = 0; Nix < icList - 1; Nix++)
|
|
{
|
|
if (pszList[Nix] > cMax)
|
|
cMax = pszList[Nix];
|
|
|
|
szIndex[0] = pszList[Nix];
|
|
|
|
lRet = RegQueryValueExW(hkey, szIndex, NULL, &dwType, NULL, &icCmd);
|
|
if (lRet != ERROR_SUCCESS || dwType != REG_SZ)
|
|
{
|
|
TRACE("Unable to grab size of index, error %d\n", GetLastError());
|
|
continue;
|
|
}
|
|
|
|
if (pszCmd)
|
|
{
|
|
pszTmp = (WCHAR*)HeapReAlloc(GetProcessHeap(), 0, pszCmd, icCmd);
|
|
if (!pszTmp)
|
|
{
|
|
TRACE("HeapReAlloc failed to reallocate %d bytes\n", icCmd);
|
|
continue;
|
|
}
|
|
pszCmd = pszTmp;
|
|
}
|
|
else
|
|
{
|
|
pszCmd = (WCHAR*)HeapAlloc(GetProcessHeap(), 0, icCmd);
|
|
if (!pszCmd)
|
|
{
|
|
TRACE("HeapAlloc failed to allocate %d bytes\n", icCmd);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
lRet = RegQueryValueExW(hkey, szIndex, NULL, NULL, (LPBYTE)pszCmd, &icCmd);
|
|
if (lRet != ERROR_SUCCESS)
|
|
{
|
|
TRACE("Unable to grab index, error %d\n", GetLastError());
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Generally the command string will end up with "\\1".
|
|
* Find the last backslash in the string and NULL-terminate.
|
|
* Windows does not seem to check for what comes next, so that
|
|
* a command of the form:
|
|
* c:\\my_dir\\myfile.exe
|
|
* will be cut just after "my_dir", whereas a command of the form:
|
|
* c:\\my_dir\\myfile.exe\\1
|
|
* will be cut just after "myfile.exe".
|
|
*/
|
|
pszTmp = wcsrchr(pszCmd, L'\\');
|
|
if (pszTmp)
|
|
*pszTmp = L'\0';
|
|
|
|
/*
|
|
* In the following we try to add pszLatest to the MRU list.
|
|
* We suppose that our caller has already correctly allocated
|
|
* the string with enough space for us to append a "\\1".
|
|
*
|
|
* FIXME: TODO! (At the moment we don't append it!)
|
|
*/
|
|
|
|
if (pszLatest)
|
|
{
|
|
if (_wcsicmp(pszCmd, pszLatest) == 0)
|
|
{
|
|
SendMessageW(hCb, CB_INSERTSTRING, 0, (LPARAM)pszCmd);
|
|
SetWindowTextW(hCb, pszCmd);
|
|
SendMessageW(hCb, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
|
|
|
|
cMatch = pszList[Nix];
|
|
memmove(&pszList[1], pszList, Nix * sizeof(WCHAR));
|
|
pszList[0] = cMatch;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (icList - 1 != 26 || icList - 2 != Nix || cMatch || pszLatest == NULL)
|
|
{
|
|
SendMessageW(hCb, CB_ADDSTRING, 0, (LPARAM)pszCmd);
|
|
if (!Nix && fShowDefault)
|
|
{
|
|
SetWindowTextW(hCb, pszCmd);
|
|
SendMessageW(hCb, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SendMessageW(hCb, CB_INSERTSTRING, 0, (LPARAM)pszLatest);
|
|
SetWindowTextW(hCb, pszLatest);
|
|
SendMessageW(hCb, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
|
|
|
|
cMatch = pszList[Nix];
|
|
memmove(&pszList[1], pszList, Nix * sizeof(WCHAR));
|
|
pszList[0] = cMatch;
|
|
szIndex[0] = cMatch;
|
|
|
|
wcscpy(&pszLatest[cchLatest], L"\\1");
|
|
RegSetValueExW(hkey, szIndex, 0, REG_SZ, (LPBYTE)pszLatest, (cchLatest + 2 + 1) * sizeof(WCHAR));
|
|
pszLatest[cchLatest] = L'\0';
|
|
}
|
|
}
|
|
|
|
if (!cMatch && pszLatest != NULL)
|
|
{
|
|
SendMessageW(hCb, CB_INSERTSTRING, 0, (LPARAM)pszLatest);
|
|
SetWindowTextW(hCb, pszLatest);
|
|
SendMessageW(hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1));
|
|
|
|
cMatch = ++cMax;
|
|
|
|
if (pszList)
|
|
{
|
|
pszTmp = (WCHAR*)HeapReAlloc(GetProcessHeap(), 0, pszList, (++icList) * sizeof(WCHAR));
|
|
if (!pszTmp)
|
|
{
|
|
TRACE("HeapReAlloc failed to reallocate enough bytes\n");
|
|
goto Cleanup;
|
|
}
|
|
pszList = pszTmp;
|
|
}
|
|
else
|
|
{
|
|
pszList = (WCHAR*)HeapAlloc(GetProcessHeap(), 0, (++icList) * sizeof(WCHAR));
|
|
if (!pszList)
|
|
{
|
|
TRACE("HeapAlloc failed to allocate enough bytes\n");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
memmove(&pszList[1], pszList, (icList - 1) * sizeof(WCHAR));
|
|
pszList[0] = cMatch;
|
|
szIndex[0] = cMatch;
|
|
|
|
wcscpy(&pszLatest[cchLatest], L"\\1");
|
|
RegSetValueExW(hkey, szIndex, 0, REG_SZ, (LPBYTE)pszLatest, (cchLatest + 2 + 1) * sizeof(WCHAR));
|
|
pszLatest[cchLatest] = L'\0';
|
|
}
|
|
|
|
Cleanup:
|
|
RegSetValueExW(hkey, L"MRUList", 0, REG_SZ, (LPBYTE)pszList, (wcslen(pszList) + 1) * sizeof(WCHAR));
|
|
|
|
HeapFree(GetProcessHeap(), 0, pszCmd);
|
|
HeapFree(GetProcessHeap(), 0, pszList);
|
|
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
* ConfirmDialog [internal]
|
|
*
|
|
* Put up a confirm box, return TRUE if the user confirmed
|
|
*/
|
|
static BOOL ConfirmDialog(HWND hWndOwner, UINT PromptId, UINT TitleId)
|
|
{
|
|
WCHAR Prompt[256];
|
|
WCHAR Title[256];
|
|
|
|
LoadStringW(shell32_hInstance, PromptId, Prompt, _countof(Prompt));
|
|
LoadStringW(shell32_hInstance, TitleId, Title, _countof(Title));
|
|
return MessageBoxW(hWndOwner, Prompt, Title, MB_YESNO | MB_ICONQUESTION) == IDYES;
|
|
}
|
|
|
|
typedef HRESULT (WINAPI *tShellDimScreen)(IUnknown** Unknown, HWND* hWindow);
|
|
|
|
BOOL
|
|
CallShellDimScreen(IUnknown** pUnknown, HWND* hWindow)
|
|
{
|
|
static tShellDimScreen ShellDimScreen;
|
|
static BOOL Initialized = FALSE;
|
|
if (!Initialized)
|
|
{
|
|
HMODULE mod = LoadLibraryW(L"msgina.dll");
|
|
ShellDimScreen = (tShellDimScreen)GetProcAddress(mod, (LPCSTR)16);
|
|
Initialized = TRUE;
|
|
}
|
|
|
|
HRESULT hr = E_FAIL;
|
|
if (ShellDimScreen)
|
|
hr = ShellDimScreen(pUnknown, hWindow);
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
|
|
/* Used to get the shutdown privilege */
|
|
static BOOL
|
|
EnablePrivilege(LPCWSTR lpszPrivilegeName, BOOL bEnablePrivilege)
|
|
{
|
|
BOOL Success;
|
|
HANDLE hToken;
|
|
TOKEN_PRIVILEGES tp;
|
|
|
|
Success = OpenProcessToken(GetCurrentProcess(),
|
|
TOKEN_ADJUST_PRIVILEGES,
|
|
&hToken);
|
|
if (!Success) return Success;
|
|
|
|
Success = LookupPrivilegeValueW(NULL,
|
|
lpszPrivilegeName,
|
|
&tp.Privileges[0].Luid);
|
|
if (!Success) goto Quit;
|
|
|
|
tp.PrivilegeCount = 1;
|
|
tp.Privileges[0].Attributes = (bEnablePrivilege ? SE_PRIVILEGE_ENABLED : 0);
|
|
|
|
Success = AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
|
|
|
|
Quit:
|
|
CloseHandle(hToken);
|
|
return Success;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* RestartDialogEx [SHELL32.730]
|
|
*/
|
|
|
|
int WINAPI RestartDialogEx(HWND hWndOwner, LPCWSTR lpwstrReason, DWORD uFlags, DWORD uReason)
|
|
{
|
|
TRACE("(%p)\n", hWndOwner);
|
|
|
|
CComPtr<IUnknown> fadeHandler;
|
|
HWND parent;
|
|
|
|
if (!CallShellDimScreen(&fadeHandler, &parent))
|
|
parent = hWndOwner;
|
|
|
|
/* FIXME: use lpwstrReason */
|
|
if (ConfirmDialog(parent, IDS_RESTART_PROMPT, IDS_RESTART_TITLE))
|
|
{
|
|
EnablePrivilege(L"SeShutdownPrivilege", TRUE);
|
|
ExitWindowsEx(EWX_REBOOT, uReason);
|
|
EnablePrivilege(L"SeShutdownPrivilege", FALSE);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Functions and macros used for fancy log off dialog box */
|
|
#define IS_PRODUCT_VERSION_WORKSTATION 0x300
|
|
#define FRIENDLY_LOGOFF_IS_NOT_ENFORCED 0x0
|
|
|
|
#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 2
|
|
|
|
/* After determining the button as well as its state paint the image strip bitmap using these predefined positions */
|
|
#define BUTTON_SWITCH_USER 0
|
|
#define BUTTON_SWITCH_USER_PRESSED (CY_BITMAP + BUTTON_SWITCH_USER)
|
|
#define BUTTON_SWITCH_USER_FOCUSED (CY_BITMAP + BUTTON_SWITCH_USER_PRESSED)
|
|
#define BUTTON_LOG_OFF (CY_BITMAP + BUTTON_SWITCH_USER_FOCUSED)
|
|
#define BUTTON_LOG_OFF_PRESSED (CY_BITMAP + BUTTON_LOG_OFF)
|
|
#define BUTTON_LOG_OFF_FOCUSED (CY_BITMAP + BUTTON_LOG_OFF_PRESSED)
|
|
#define BUTTON_SWITCH_USER_DISABLED (CY_BITMAP + BUTTON_LOG_OFF_FOCUSED) // Temporary
|
|
|
|
/* For bIsButtonHot */
|
|
#define LOG_OFF_BUTTON_HOT 0
|
|
#define SWITCH_USER_BUTTON_HOT 1
|
|
|
|
BOOL DrawIconOnOwnerDrawnButtons(DRAWITEMSTRUCT* pdis, PLOGOFF_DLG_CONTEXT pContext)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
HDC hdcMem = NULL;
|
|
HBITMAP hbmOld = NULL;
|
|
int y = 0;
|
|
RECT rect;
|
|
|
|
hdcMem = CreateCompatibleDC(pdis->hDC);
|
|
hbmOld = (HBITMAP)SelectObject(hdcMem, pContext->hImageStrip);
|
|
rect = pdis->rcItem;
|
|
|
|
/* Check the button ID for relevant bitmap to be used */
|
|
switch (pdis->CtlID)
|
|
{
|
|
case IDC_LOG_OFF_BUTTON:
|
|
{
|
|
switch (pdis->itemAction)
|
|
{
|
|
case ODA_DRAWENTIRE:
|
|
case ODA_FOCUS:
|
|
case ODA_SELECT:
|
|
{
|
|
y = BUTTON_LOG_OFF;
|
|
if (pdis->itemState & ODS_SELECTED)
|
|
{
|
|
y = BUTTON_LOG_OFF_PRESSED;
|
|
}
|
|
else if (pContext->bIsButtonHot[LOG_OFF_BUTTON_HOT] || (pdis->itemState & ODS_FOCUS))
|
|
{
|
|
y = BUTTON_LOG_OFF_FOCUSED;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IDC_SWITCH_USER_BUTTON:
|
|
{
|
|
switch (pdis->itemAction)
|
|
{
|
|
case ODA_DRAWENTIRE:
|
|
case ODA_FOCUS:
|
|
case ODA_SELECT:
|
|
{
|
|
y = BUTTON_SWITCH_USER;
|
|
if (pdis->itemState & ODS_SELECTED)
|
|
{
|
|
y = BUTTON_SWITCH_USER_PRESSED;
|
|
}
|
|
else if (pContext->bIsButtonHot[SWITCH_USER_BUTTON_HOT] || (pdis->itemState & ODS_FOCUS))
|
|
{
|
|
y = BUTTON_SWITCH_USER_FOCUSED;
|
|
}
|
|
|
|
/*
|
|
* Since switch user functionality isn't implemented yet therefore the button has been disabled
|
|
* temporarily hence show the disabled state
|
|
*/
|
|
else if (pdis->itemState & ODS_DISABLED)
|
|
{
|
|
y = BUTTON_SWITCH_USER_DISABLED;
|
|
}
|
|
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;
|
|
}
|
|
|
|
INT_PTR CALLBACK OwnerDrawButtonSubclass(HWND hButton, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
PLOGOFF_DLG_CONTEXT pContext;
|
|
pContext = (PLOGOFF_DLG_CONTEXT)GetWindowLongPtrW(GetParent(hButton), GWLP_USERDATA);
|
|
|
|
int buttonID = GetDlgCtrlID(hButton);
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
HWND hwndTarget = NULL;
|
|
POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};;
|
|
|
|
if (GetCapture() != hButton)
|
|
{
|
|
SetCapture(hButton);
|
|
|
|
switch (buttonID)
|
|
{
|
|
case IDC_LOG_OFF_BUTTON:
|
|
{
|
|
pContext->bIsButtonHot[LOG_OFF_BUTTON_HOT] = TRUE;
|
|
break;
|
|
}
|
|
case IDC_SWITCH_USER_BUTTON:
|
|
{
|
|
pContext->bIsButtonHot[SWITCH_USER_BUTTON_HOT] = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
SetCursor(LoadCursorW(NULL, IDC_HAND));
|
|
}
|
|
|
|
ClientToScreen(hButton, &pt);
|
|
hwndTarget = WindowFromPoint(pt);
|
|
|
|
if (hwndTarget != hButton)
|
|
{
|
|
ReleaseCapture();
|
|
|
|
switch (buttonID)
|
|
{
|
|
case IDC_LOG_OFF_BUTTON:
|
|
{
|
|
pContext->bIsButtonHot[LOG_OFF_BUTTON_HOT] = FALSE;
|
|
break;
|
|
}
|
|
case IDC_SWITCH_USER_BUTTON:
|
|
{
|
|
pContext->bIsButtonHot[SWITCH_USER_BUTTON_HOT] = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
HWND hwndTool = NULL, hwndTip = NULL;
|
|
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, shell32_hInstance, NULL);
|
|
|
|
/* Associate the tooltip with the tool. */
|
|
LoadStringW(shell32_hInstance, detailID, szBuffer, _countof(szBuffer));
|
|
tool.lpszText = szBuffer;
|
|
SendMessageW(hwndTip, TTM_ADDTOOLW, 0, (LPARAM)&tool);
|
|
LoadStringW(shell32_hInstance, titleID, szBuffer, _countof(szBuffer));
|
|
SendMessageW(hwndTip, TTM_SETTITLEW, TTI_NONE, (LPARAM)szBuffer);
|
|
SendMessageW(hwndTip, TTM_SETMAXTIPWIDTH, 0, 250);
|
|
}
|
|
|
|
VOID EndFriendlyDialog(HWND hwnd, PLOGOFF_DLG_CONTEXT pContext)
|
|
{
|
|
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(hwnd, IDC_LOG_OFF_BUTTON + i),
|
|
GWLP_WNDPROC,
|
|
(LONG_PTR)pContext->OldButtonProc);
|
|
}
|
|
}
|
|
|
|
static BOOL IsFriendlyUIActive(VOID)
|
|
{
|
|
DWORD dwType = 0, dwValue = 0, dwSize = 0;
|
|
HKEY hKey = NULL;
|
|
LONG lRet = 0;
|
|
|
|
lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"SYSTEM\\CurrentControlSet\\Control\\Windows",
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey);
|
|
if (lRet != ERROR_SUCCESS)
|
|
return FALSE;
|
|
|
|
/* First check an optional ReactOS specific override, that Windows does not check.
|
|
We use this to allow users pairing 'Server'-configuration with FriendlyLogoff.
|
|
Otherwise users would have to change CSDVersion or LogonType (side-effects AppCompat) */
|
|
dwValue = 0;
|
|
dwSize = sizeof(dwValue);
|
|
lRet = RegQueryValueExW(hKey,
|
|
L"EnforceFriendlyLogoff",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize);
|
|
|
|
if (lRet == ERROR_SUCCESS && dwType == REG_DWORD && dwValue != FRIENDLY_LOGOFF_IS_NOT_ENFORCED)
|
|
{
|
|
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 != IS_PRODUCT_VERSION_WORKSTATION)
|
|
{
|
|
/* 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 VOID FancyLogoffOnInit(HWND hwnd, PLOGOFF_DLG_CONTEXT pContext)
|
|
{
|
|
HDC hdc = NULL;
|
|
LONG lfHeight = NULL;
|
|
|
|
hdc = GetDC(NULL);
|
|
lfHeight = -MulDiv(FONT_POINT_SIZE, GetDeviceCaps(hdc, LOGPIXELSY), 72);
|
|
ReleaseDC(NULL, hdc);
|
|
pContext->hfFont = CreateFontW(lfHeight, 0, 0, 0, FW_MEDIUM, FALSE, 0, 0, 0, 0, 0, 0, 0, L"MS Shell Dlg");
|
|
SendDlgItemMessageW(hwnd, IDC_LOG_OFF_TEXT_STATIC, WM_SETFONT, (WPARAM)pContext->hfFont, TRUE);
|
|
|
|
pContext->hBrush = CreateSolidBrush(DARK_GREY_COLOR);
|
|
|
|
pContext->hImageStrip = LoadBitmapW(shell32_hInstance, MAKEINTRESOURCEW(IDB_IMAGE_STRIP));
|
|
|
|
/* Gather old button func */
|
|
pContext->OldButtonProc = (WNDPROC)GetWindowLongPtrW(GetDlgItem(hwnd, IDC_LOG_OFF_BUTTON), GWLP_WNDPROC);
|
|
|
|
/* Set bIsButtonHot to false, create tooltips for each buttons and subclass the buttons */
|
|
for (int i = 0; i < NUMBER_OF_BUTTONS; i++)
|
|
{
|
|
pContext->bIsButtonHot[i] = FALSE;
|
|
SetWindowLongPtrW(GetDlgItem(hwnd, IDC_LOG_OFF_BUTTON + i),
|
|
GWLP_WNDPROC,
|
|
(LONG_PTR)OwnerDrawButtonSubclass);
|
|
CreateToolTipForButtons(IDC_LOG_OFF_BUTTON + i,
|
|
IDS_LOG_OFF_DESC + i,
|
|
hwnd,
|
|
IDS_LOG_OFF_TITLE + i);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
* LogOffDialogProc
|
|
*
|
|
* NOTES: Used to make the Log Off dialog work
|
|
*/
|
|
INT_PTR CALLBACK LogOffDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
DRAWITEMSTRUCT* pdis = (DRAWITEMSTRUCT*)lParam;
|
|
PLOGOFF_DLG_CONTEXT pContext;
|
|
pContext = (PLOGOFF_DLG_CONTEXT)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
pContext = (PLOGOFF_DLG_CONTEXT)lParam;
|
|
SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)pContext);
|
|
|
|
if (pContext->bFriendlyUI)
|
|
FancyLogoffOnInit(hwnd, pContext);
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
if (pContext->bFriendlyUI)
|
|
EndFriendlyDialog(hwnd, pContext);
|
|
return TRUE;
|
|
|
|
case WM_ACTIVATE:
|
|
{
|
|
/*
|
|
* If the user deactivates the log-off dialog (it loses its focus
|
|
* while the dialog is not being closed), then destroy the dialog
|
|
* and cancel user logoff.
|
|
*/
|
|
if (LOWORD(wParam) == WA_INACTIVE)
|
|
{
|
|
if (!pContext->bCloseDlg)
|
|
{
|
|
pContext->bCloseDlg = TRUE;
|
|
EndDialog(hwnd, IDCANCEL);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
case WM_CLOSE:
|
|
pContext->bCloseDlg = TRUE;
|
|
EndDialog(hwnd, IDCANCEL);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDC_LOG_OFF_BUTTON:
|
|
case IDOK:
|
|
ExitWindowsEx(EWX_LOGOFF, 0);
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
pContext->bCloseDlg = TRUE;
|
|
EndDialog(hwnd, IDCANCEL);
|
|
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_LOG_OFF_TEXT_STATIC:
|
|
SetTextColor(hdcStatic, DARK_GREY_COLOR);
|
|
SetBkMode(hdcStatic, TRANSPARENT);
|
|
return (INT_PTR)GetStockObject(HOLLOW_BRUSH);
|
|
|
|
case IDC_LOG_OFF_STATIC:
|
|
case IDC_SWITCH_USER_STATIC:
|
|
SetTextColor(hdcStatic, LIGHT_GREY_COLOR);
|
|
SetBkMode(hdcStatic, TRANSPARENT);
|
|
return (LONG_PTR)pContext->hBrush;
|
|
}
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case WM_DRAWITEM:
|
|
{
|
|
/* Draw bitmaps on required buttons */
|
|
switch (pdis->CtlID)
|
|
{
|
|
case IDC_LOG_OFF_BUTTON:
|
|
case IDC_SWITCH_USER_BUTTON:
|
|
return DrawIconOnOwnerDrawnButtons(pdis, pContext);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* LogoffWindowsDialog [SHELL32.54]
|
|
*/
|
|
|
|
EXTERN_C int WINAPI LogoffWindowsDialog(HWND hWndOwner)
|
|
{
|
|
CComPtr<IUnknown> fadeHandler;
|
|
HWND parent = NULL;
|
|
DWORD LogoffDialogID = IDD_LOG_OFF;
|
|
LOGOFF_DLG_CONTEXT Context = {0};
|
|
|
|
if (!CallShellDimScreen(&fadeHandler, &parent))
|
|
parent = hWndOwner;
|
|
|
|
Context.bFriendlyUI = IsFriendlyUIActive();
|
|
if (Context.bFriendlyUI)
|
|
{
|
|
LogoffDialogID = IDD_LOG_OFF_FANCY;
|
|
}
|
|
|
|
DialogBoxParamW(shell32_hInstance,
|
|
MAKEINTRESOURCEW(LogoffDialogID),
|
|
parent,
|
|
LogOffDialogProc,
|
|
(LPARAM)&Context);
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* RestartDialog [SHELL32.59]
|
|
*/
|
|
|
|
int WINAPI RestartDialog(HWND hWndOwner, LPCWSTR lpstrReason, DWORD uFlags)
|
|
{
|
|
return RestartDialogEx(hWndOwner, lpstrReason, uFlags, 0);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* ExitWindowsDialog_backup
|
|
*
|
|
* NOTES
|
|
* Used as a backup solution to shutdown the OS in case msgina.dll
|
|
* somehow cannot be found.
|
|
*/
|
|
VOID ExitWindowsDialog_backup(HWND hWndOwner)
|
|
{
|
|
TRACE("(%p)\n", hWndOwner);
|
|
|
|
if (ConfirmDialog(hWndOwner, IDS_SHUTDOWN_PROMPT, IDS_SHUTDOWN_TITLE))
|
|
{
|
|
EnablePrivilege(L"SeShutdownPrivilege", TRUE);
|
|
ExitWindowsEx(EWX_SHUTDOWN, 0);
|
|
EnablePrivilege(L"SeShutdownPrivilege", FALSE);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
* ExitWindowsDialog [SHELL32.60]
|
|
*
|
|
* NOTES
|
|
* exported by ordinal
|
|
*/
|
|
/*
|
|
* TODO:
|
|
* - Implement the ability to show either the Welcome Screen or the classic dialog boxes based upon the
|
|
* registry value: SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\LogonType.
|
|
*/
|
|
void WINAPI ExitWindowsDialog(HWND hWndOwner)
|
|
{
|
|
typedef DWORD (WINAPI *ShellShFunc)(HWND hWndParent, LPCWSTR pUserName, DWORD dwExcludeOptions);
|
|
HINSTANCE msginaDll = LoadLibraryW(L"msgina.dll");
|
|
|
|
TRACE("(%p)\n", hWndOwner);
|
|
|
|
CComPtr<IUnknown> fadeHandler;
|
|
HWND parent;
|
|
if (!CallShellDimScreen(&fadeHandler, &parent))
|
|
parent = hWndOwner;
|
|
|
|
/* If the DLL cannot be found for any reason, then it simply uses a
|
|
dialog box to ask if the user wants to shut down the computer. */
|
|
if (!msginaDll)
|
|
{
|
|
TRACE("Unable to load msgina.dll.\n");
|
|
ExitWindowsDialog_backup(parent);
|
|
return;
|
|
}
|
|
|
|
ShellShFunc pShellShutdownDialog = (ShellShFunc)GetProcAddress(msginaDll, "ShellShutdownDialog");
|
|
if (pShellShutdownDialog)
|
|
{
|
|
/* Actually call the function */
|
|
DWORD returnValue = pShellShutdownDialog(parent, NULL, 0);
|
|
|
|
switch (returnValue)
|
|
{
|
|
case 0x01: /* Log off user */
|
|
{
|
|
ExitWindowsEx(EWX_LOGOFF, 0);
|
|
break;
|
|
}
|
|
case 0x02: /* Shut down */
|
|
{
|
|
EnablePrivilege(L"SeShutdownPrivilege", TRUE);
|
|
ExitWindowsEx(EWX_SHUTDOWN, 0);
|
|
EnablePrivilege(L"SeShutdownPrivilege", FALSE);
|
|
break;
|
|
}
|
|
case 0x03: /* Install Updates/Shutdown (?) */
|
|
{
|
|
break;
|
|
}
|
|
case 0x04: /* Reboot */
|
|
{
|
|
EnablePrivilege(L"SeShutdownPrivilege", TRUE);
|
|
ExitWindowsEx(EWX_REBOOT, 0);
|
|
EnablePrivilege(L"SeShutdownPrivilege", FALSE);
|
|
break;
|
|
}
|
|
case 0x10: /* Sleep */
|
|
{
|
|
if (IsPwrSuspendAllowed())
|
|
{
|
|
EnablePrivilege(L"SeShutdownPrivilege", TRUE);
|
|
SetSuspendState(FALSE, FALSE, FALSE);
|
|
EnablePrivilege(L"SeShutdownPrivilege", FALSE);
|
|
}
|
|
break;
|
|
}
|
|
case 0x40: /* Hibernate */
|
|
{
|
|
if (IsPwrHibernateAllowed())
|
|
{
|
|
EnablePrivilege(L"SeShutdownPrivilege", TRUE);
|
|
SetSuspendState(TRUE, FALSE, TRUE);
|
|
EnablePrivilege(L"SeShutdownPrivilege", FALSE);
|
|
}
|
|
break;
|
|
}
|
|
/* If the option is any other value */
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* If the function cannot be found, then revert to using the backup solution */
|
|
TRACE("Unable to find the 'ShellShutdownDialog' function");
|
|
ExitWindowsDialog_backup(parent);
|
|
}
|
|
|
|
FreeLibrary(msginaDll);
|
|
}
|