mirror of
https://github.com/reactos/reactos.git
synced 2025-01-05 22:12:46 +00:00
1a1025011f
Fix wrong MAKEINTRESOURCEW() macro usage cases in LoadCursorW() system-wide. MSDN documentation for this function states we need to use MAKEINTRESOURCEW() macro only for internal application-defined cursors (loaded from the app instance specified by hInstance parameter), but not for system-defined cursors (those begin with IDC_* prefix), in case when hInstance is NULL. So get rid from MAKEINTRESOURCEW() macro usage for all cases when hInstance parameter is NULL. And on the contrary, when hInstance is valid and points to the application instance, then use it for the resource identifier.
1694 lines
55 KiB
C++
1694 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;
|
|
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;
|
|
}
|
|
}
|
|
|
|
// Set the text and reset the edit control's modification flag
|
|
SetDlgItemTextW(hwndDlg, IDC_EDIT_PATH, pIconContext->szPath);
|
|
SendDlgItemMessage(hwndDlg, IDC_EDIT_PATH, EM_SETMODIFY, FALSE, 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];
|
|
CStringW strTitle;
|
|
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 */
|
|
if (SendDlgItemMessage(hwndDlg, IDC_EDIT_PATH, EM_GETMODIFY, 0, 0))
|
|
{
|
|
/* Reset the edit control's modification flag and retrieve the text */
|
|
SendDlgItemMessage(hwndDlg, IDC_EDIT_PATH, EM_SETMODIFY, FALSE, 0);
|
|
GetDlgItemTextW(hwndDlg, IDC_EDIT_PATH, szText, _countof(szText));
|
|
|
|
// Load the icons
|
|
if (!DoLoadIcons(hwndDlg, pIconContext, szText))
|
|
NoIconsInFile(hwndDlg, pIconContext);
|
|
|
|
// Set the selection
|
|
SendMessageW(pIconContext->hDlgCtrl, LB_SETCURSEL, 0, 0);
|
|
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
|
|
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);
|
|
|
|
// Set the selection
|
|
SendMessageW(pIconContext->hDlgCtrl, LB_SETCURSEL, 0, 0);
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
int res;
|
|
WCHAR szExpandedPath[MAX_PATH];
|
|
|
|
// Initialize the dialog
|
|
PICK_ICON_CONTEXT IconContext = { NULL };
|
|
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;
|
|
NMRUNFILEDLGW nmrfd;
|
|
|
|
ic = GetWindowTextLengthW(htxt);
|
|
if (ic == 0)
|
|
{
|
|
EndDialog(hwnd, IDCANCEL);
|
|
return TRUE;
|
|
}
|
|
|
|
ZeroMemory(&sei, sizeof(sei));
|
|
sei.cbSize = sizeof(sei);
|
|
|
|
/*
|
|
* 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 (SUCCEEDED(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);
|
|
}
|
|
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 revelant 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_CLOSE:
|
|
EndDialog(hwnd, IDCANCEL);
|
|
break;
|
|
|
|
/*
|
|
* If the user deactivates the log off dialog (it loses its focus
|
|
* while the dialog is not being closed), then destroy the dialog
|
|
* box.
|
|
*/
|
|
case WM_ACTIVATE:
|
|
{
|
|
if (LOWORD(wParam) == WA_INACTIVE)
|
|
{
|
|
EndDialog(hwnd, IDCANCEL);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDC_LOG_OFF_BUTTON:
|
|
case IDOK:
|
|
ExitWindowsEx(EWX_LOGOFF, 0);
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hwnd, IDCANCEL);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
if (pContext->bFriendlyUI)
|
|
EndFriendlyDialog(hwnd, pContext);
|
|
return TRUE;
|
|
|
|
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;
|
|
|
|
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 hParent, WCHAR *Username, BOOL bHideLogoff);
|
|
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, FALSE);
|
|
|
|
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);
|
|
}
|