shell32: Properly double-null-terminate the filter in the run/browse dialog.
[wine] / dlls / shell32 / dialogs.c
1 /*
2  *      common shell dialogs
3  *
4  * Copyright 2000 Juergen Schmied
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <string.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include "winerror.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winreg.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "commdlg.h"
34 #include "wine/debug.h"
35
36 #include "shellapi.h"
37 #include "shlobj.h"
38 #include "shell32_main.h"
39 #include "shresdef.h"
40 #include "undocshell.h"
41
42 typedef struct
43     {
44         HWND hwndOwner ;
45         HICON hIcon ;
46         LPCWSTR lpstrDirectory ;
47         LPCWSTR lpstrTitle ;
48         LPCWSTR lpstrDescription ;
49         UINT uFlags ;
50     } RUNFILEDLGPARAMS ;
51
52 typedef BOOL (WINAPI * LPFNOFN) (OPENFILENAMEW *) ;
53
54 WINE_DEFAULT_DEBUG_CHANNEL(shell);
55 static INT_PTR CALLBACK RunDlgProc (HWND, UINT, WPARAM, LPARAM) ;
56 static void FillList (HWND, char *, BOOL) ;
57
58
59 /*************************************************************************
60  * PickIconDlg                                  [SHELL32.62]
61  *
62  */
63 INT WINAPI PickIconDlg(
64         HWND hwndOwner,
65         LPSTR lpstrFile,
66         DWORD nMaxFile,
67         LPDWORD lpdwIconIndex)
68 {
69         FIXME("(%p,%s,%08x,%p):stub.\n",
70           hwndOwner, lpstrFile, nMaxFile,lpdwIconIndex);
71         return 0xffffffff;
72 }
73
74 /*************************************************************************
75  * RunFileDlgW                                  [internal]
76  *
77  * The Unicode function that is available as ordinal 61 on Windows NT/2000/XP/...
78  *
79  * SEE ALSO
80  *   RunFileDlgAW
81  */
82 static void RunFileDlgW(
83         HWND hwndOwner,
84         HICON hIcon,
85         LPCWSTR lpstrDirectory,
86         LPCWSTR lpstrTitle,
87         LPCWSTR lpstrDescription,
88         UINT uFlags)
89 {
90     static const WCHAR resnameW[] = {'S','H','E','L','L','_','R','U','N','_','D','L','G',0};
91     RUNFILEDLGPARAMS rfdp;
92     HRSRC hRes;
93     LPVOID template;
94     TRACE("\n");
95
96     rfdp.hwndOwner        = hwndOwner;
97     rfdp.hIcon            = hIcon;
98     rfdp.lpstrDirectory   = lpstrDirectory;
99     rfdp.lpstrTitle       = lpstrTitle;
100     rfdp.lpstrDescription = lpstrDescription;
101     rfdp.uFlags           = uFlags;
102
103     if (!(hRes = FindResourceW(shell32_hInstance, resnameW, (LPWSTR)RT_DIALOG)) ||
104         !(template = LoadResource(shell32_hInstance, hRes)))
105     {
106         ERR("Couldn't load SHELL_RUN_DLG resource\n");
107         ShellMessageBoxW(shell32_hInstance, hwndOwner, MAKEINTRESOURCEW(IDS_RUNDLG_ERROR), NULL, MB_OK | MB_ICONERROR);
108         return;
109     }
110
111     DialogBoxIndirectParamW(shell32_hInstance,
112                             template, hwndOwner, RunDlgProc, (LPARAM)&rfdp);
113
114 }
115
116 /* find the directory that contains the file being run */
117 static LPWSTR RunDlg_GetParentDir(LPCWSTR cmdline)
118 {
119     const WCHAR *src;
120     WCHAR *dest, *result, *result_end=NULL;
121     static const WCHAR dotexeW[] = {'.','e','x','e',0};
122
123     result = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(strlenW(cmdline)+5));
124
125     src = cmdline;
126     dest = result;
127
128     if (*src == '"')
129     {
130         src++;
131         while (*src && *src != '"')
132         {
133             if (*src == '\\')
134                 result_end = dest;
135             *dest++ = *src++;
136         }
137     }
138     else {
139         while (*src)
140         {
141             if (isspaceW(*src))
142             {
143                 *dest = 0;
144                 if (INVALID_FILE_ATTRIBUTES != GetFileAttributesW(result))
145                     break;
146                 strcatW(dest, dotexeW);
147                 if (INVALID_FILE_ATTRIBUTES != GetFileAttributesW(result))
148                     break;
149             }
150             else if (*src == '\\')
151                 result_end = dest;
152             *dest++ = *src++;
153         }
154     }
155
156     if (result_end)
157     {
158         *result_end = 0;
159         return result;
160     }
161     else
162     {
163         HeapFree(GetProcessHeap(), 0, result);
164         return NULL;
165     }
166 }
167
168 /* Dialog procedure for RunFileDlg */
169 static INT_PTR CALLBACK RunDlgProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
170     {
171     RUNFILEDLGPARAMS *prfdp = (RUNFILEDLGPARAMS *)GetWindowLongPtrW(hwnd, DWLP_USER);
172
173     switch (message)
174         {
175         case WM_INITDIALOG :
176             prfdp = (RUNFILEDLGPARAMS *)lParam ;
177             SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR)prfdp);
178
179             if (prfdp->lpstrTitle)
180                 SetWindowTextW(hwnd, prfdp->lpstrTitle);
181             if (prfdp->lpstrDescription)
182                 SetWindowTextW(GetDlgItem(hwnd, IDC_RUNDLG_DESCRIPTION), prfdp->lpstrDescription);
183             if (prfdp->uFlags & RFF_NOBROWSE)
184             {
185                 HWND browse = GetDlgItem(hwnd, IDC_RUNDLG_BROWSE);
186                 ShowWindow(browse, SW_HIDE);
187                 EnableWindow(browse, FALSE);
188             }
189             if (prfdp->uFlags & RFF_NOLABEL)
190                 ShowWindow(GetDlgItem(hwnd, IDC_RUNDLG_LABEL), SW_HIDE);
191             if (prfdp->uFlags & RFF_CALCDIRECTORY)
192                 FIXME("RFF_CALCDIRECTORY not supported\n");
193
194             if (prfdp->hIcon == NULL)
195                 prfdp->hIcon = LoadIconW(NULL, (LPCWSTR)IDI_WINLOGO);
196             SendMessageW(hwnd, WM_SETICON, ICON_BIG, (LPARAM)prfdp->hIcon);
197             SendMessageW(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)prfdp->hIcon);
198             SendMessageW(GetDlgItem(hwnd, IDC_RUNDLG_ICON), STM_SETICON, (WPARAM)prfdp->hIcon, 0);
199
200             FillList (GetDlgItem (hwnd, IDC_RUNDLG_EDITPATH), NULL, (prfdp->uFlags & RFF_NODEFAULT) == 0) ;
201             SetFocus (GetDlgItem (hwnd, IDC_RUNDLG_EDITPATH)) ;
202             return TRUE ;
203
204         case WM_COMMAND :
205             switch (LOWORD (wParam))
206                 {
207                 case IDOK :
208                     {
209                     int ic ;
210                     HWND htxt = GetDlgItem (hwnd, IDC_RUNDLG_EDITPATH);
211                     if ((ic = GetWindowTextLengthW (htxt)))
212                         {
213                         WCHAR *psz, *parent=NULL ;
214                         SHELLEXECUTEINFOW sei ;
215
216                         ZeroMemory (&sei, sizeof(sei)) ;
217                         sei.cbSize = sizeof(sei) ;
218                         psz = HeapAlloc( GetProcessHeap(), 0, (ic + 1)*sizeof(WCHAR) );
219                         GetWindowTextW (htxt, psz, ic + 1) ;
220
221                         /* according to http://www.codeproject.com/KB/shell/runfiledlg.aspx we should send a
222                          * WM_NOTIFY before execution */
223
224                         sei.hwnd = hwnd;
225                         sei.nShow = SW_SHOWNORMAL;
226                         sei.lpFile = psz;
227
228                         if (prfdp->lpstrDirectory)
229                             sei.lpDirectory = prfdp->lpstrDirectory;
230                         else
231                             sei.lpDirectory = parent = RunDlg_GetParentDir(sei.lpFile);
232
233                         if (!ShellExecuteExW( &sei ))
234                         {
235                             HeapFree(GetProcessHeap(), 0, psz);
236                             HeapFree(GetProcessHeap(), 0, parent);
237                             SendMessageA (htxt, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
238                             return TRUE ;
239                         }
240
241                         /* FillList is still ANSI */
242                         GetWindowTextA (htxt, (LPSTR)psz, ic + 1) ;
243                         FillList (htxt, (LPSTR)psz, FALSE) ;
244
245                         HeapFree(GetProcessHeap(), 0, psz);
246                         HeapFree(GetProcessHeap(), 0, parent);
247                         EndDialog (hwnd, 0);
248                         }
249                     }
250                     return TRUE;
251
252                 case IDCANCEL :
253                     EndDialog (hwnd, 0) ;
254                     return TRUE ;
255
256                 case IDC_RUNDLG_BROWSE :
257                     {
258                     static const WCHAR filterW[] = {'%','s','%','c','*','.','e','x','e','%','c','%','s','%','c','*','.','*','%','c',0};
259                     HMODULE hComdlg = NULL ;
260                     LPFNOFN ofnProc = NULL ;
261                     static const WCHAR comdlg32W[] = {'c','o','m','d','l','g','3','2',0};
262                     WCHAR szFName[1024] = {0};
263                     WCHAR filter_exe[256], filter_all[256], filter[MAX_PATH], szCaption[MAX_PATH];
264                     OPENFILENAMEW ofn;
265
266                     LoadStringW(shell32_hInstance, IDS_RUNDLG_BROWSE_FILTER_EXE, filter_exe, 256);
267                     LoadStringW(shell32_hInstance, IDS_RUNDLG_BROWSE_FILTER_ALL, filter_all, 256);
268                     LoadStringW(shell32_hInstance, IDS_RUNDLG_BROWSE_CAPTION, szCaption, MAX_PATH);
269                     snprintfW( filter, MAX_PATH, filterW, filter_exe, 0, 0, filter_all, 0, 0 );
270
271                     ZeroMemory(&ofn, sizeof(ofn));
272                     ofn.lStructSize = sizeof(OPENFILENAMEW);
273                     ofn.hwndOwner = hwnd;
274                     ofn.lpstrFilter = filter;
275                     ofn.lpstrFile = szFName;
276                     ofn.nMaxFile = 1023;
277                     ofn.lpstrTitle = szCaption;
278                     ofn.Flags = OFN_ENABLESIZING | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
279                     ofn.lpstrInitialDir = prfdp->lpstrDirectory;
280
281                     if (NULL == (hComdlg = LoadLibraryExW (comdlg32W, NULL, 0)) ||
282                         NULL == (ofnProc = (LPFNOFN)GetProcAddress (hComdlg, "GetOpenFileNameW")))
283                     {
284                         ERR("Couldn't get GetOpenFileName function entry (lib=%p, proc=%p)\n", hComdlg, ofnProc);
285                         ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_RUNDLG_BROWSE_ERROR), NULL, MB_OK | MB_ICONERROR);
286                         return TRUE ;
287                     }
288
289                     if (ofnProc(&ofn))
290                     {
291                         SetFocus (GetDlgItem (hwnd, IDOK)) ;
292                         SetWindowTextW (GetDlgItem (hwnd, IDC_RUNDLG_EDITPATH), szFName) ;
293                         SendMessageW (GetDlgItem (hwnd, IDC_RUNDLG_EDITPATH), CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
294                         SetFocus (GetDlgItem (hwnd, IDOK)) ;
295                     }
296
297                     FreeLibrary (hComdlg) ;
298
299                     return TRUE ;
300                     }
301                 }
302             return TRUE ;
303         }
304     return FALSE ;
305     }
306
307 /* This grabs the MRU list from the registry and fills the combo for the "Run" dialog above */
308 /* fShowDefault ignored if pszLatest != NULL */
309 static void FillList (HWND hCb, char *pszLatest, BOOL fShowDefault)
310     {
311     HKEY hkey ;
312 /*    char szDbgMsg[256] = "" ; */
313     char *pszList = NULL, *pszCmd = NULL, cMatch = 0, cMax = 0x60, szIndex[2] = "-" ;
314     DWORD icList = 0, icCmd = 0 ;
315     UINT Nix ;
316
317     SendMessageA (hCb, CB_RESETCONTENT, 0, 0) ;
318
319     if (ERROR_SUCCESS != RegCreateKeyExA (
320         HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU",
321         0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL))
322         MessageBoxA (hCb, "Unable to open registry key !", "Nix", MB_OK) ;
323
324     RegQueryValueExA (hkey, "MRUList", NULL, NULL, NULL, &icList) ;
325
326     if (icList > 0)
327         {
328         pszList = HeapAlloc( GetProcessHeap(), 0, icList) ;
329         if (ERROR_SUCCESS != RegQueryValueExA (hkey, "MRUList", NULL, NULL, (LPBYTE)pszList, &icList))
330             MessageBoxA (hCb, "Unable to grab MRUList !", "Nix", MB_OK) ;
331         }
332     else
333         {
334         icList = 1 ;
335         pszList = HeapAlloc( GetProcessHeap(), 0, icList) ;
336         pszList[0] = 0 ;
337         }
338
339     for (Nix = 0 ; Nix < icList - 1 ; Nix++)
340         {
341         if (pszList[Nix] > cMax)
342             cMax = pszList[Nix] ;
343
344         szIndex[0] = pszList[Nix] ;
345
346         if (ERROR_SUCCESS != RegQueryValueExA (hkey, szIndex, NULL, NULL, NULL, &icCmd))
347             MessageBoxA (hCb, "Unable to grab size of index", "Nix", MB_OK) ;
348         if( pszCmd )
349             pszCmd = HeapReAlloc(GetProcessHeap(), 0, pszCmd, icCmd) ;
350         else
351             pszCmd = HeapAlloc(GetProcessHeap(), 0, icCmd) ;
352         if (ERROR_SUCCESS != RegQueryValueExA (hkey, szIndex, NULL, NULL, (LPBYTE)pszCmd, &icCmd))
353             MessageBoxA (hCb, "Unable to grab index", "Nix", MB_OK) ;
354
355         if (NULL != pszLatest)
356             {
357             if (!lstrcmpiA(pszCmd, pszLatest))
358                 {
359                 /*
360                 sprintf (szDbgMsg, "Found existing (%d).\n", Nix) ;
361                 MessageBoxA (hCb, szDbgMsg, "Nix", MB_OK) ;
362                 */
363                 SendMessageA (hCb, CB_INSERTSTRING, 0, (LPARAM)pszCmd) ;
364                 SetWindowTextA (hCb, pszCmd) ;
365                 SendMessageA (hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
366
367                 cMatch = pszList[Nix] ;
368                 memmove (&pszList[1], pszList, Nix) ;
369                 pszList[0] = cMatch ;
370                 continue ;
371                 }
372             }
373
374         if (26 != icList - 1 || icList - 2 != Nix || cMatch || NULL == pszLatest)
375             {
376             /*
377             sprintf (szDbgMsg, "Happily appending (%d).\n", Nix) ;
378             MessageBoxA (hCb, szDbgMsg, "Nix", MB_OK) ;
379             */
380             SendMessageA (hCb, CB_ADDSTRING, 0, (LPARAM)pszCmd) ;
381             if (!Nix && fShowDefault)
382                 {
383                 SetWindowTextA (hCb, pszCmd) ;
384                 SendMessageA (hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
385                 }
386
387             }
388         else
389             {
390             /*
391             sprintf (szDbgMsg, "Doing loop thing.\n") ;
392             MessageBoxA (hCb, szDbgMsg, "Nix", MB_OK) ;
393             */
394             SendMessageA (hCb, CB_INSERTSTRING, 0, (LPARAM)pszLatest) ;
395             SetWindowTextA (hCb, pszLatest) ;
396             SendMessageA (hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
397
398             cMatch = pszList[Nix] ;
399             memmove (&pszList[1], pszList, Nix) ;
400             pszList[0] = cMatch ;
401             szIndex[0] = cMatch ;
402             RegSetValueExA (hkey, szIndex, 0, REG_SZ, (LPBYTE)pszLatest, strlen (pszLatest) + 1) ;
403             }
404         }
405
406     if (!cMatch && NULL != pszLatest)
407         {
408         /*
409         sprintf (szDbgMsg, "Simply inserting (increasing list).\n") ;
410         MessageBoxA (hCb, szDbgMsg, "Nix", MB_OK) ;
411         */
412         SendMessageA (hCb, CB_INSERTSTRING, 0, (LPARAM)pszLatest) ;
413         SetWindowTextA (hCb, pszLatest) ;
414         SendMessageA (hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
415
416         cMatch = ++cMax ;
417         if( pszList )
418             pszList = HeapReAlloc(GetProcessHeap(), 0, pszList, ++icList) ;
419         else
420             pszList = HeapAlloc(GetProcessHeap(), 0, ++icList) ;
421         memmove (&pszList[1], pszList, icList - 1) ;
422         pszList[0] = cMatch ;
423         szIndex[0] = cMatch ;
424         RegSetValueExA (hkey, szIndex, 0, REG_SZ, (LPBYTE)pszLatest, strlen (pszLatest) + 1) ;
425         }
426
427     RegSetValueExA (hkey, "MRUList", 0, REG_SZ, (LPBYTE)pszList, strlen (pszList) + 1) ;
428
429     HeapFree( GetProcessHeap(), 0, pszCmd) ;
430     HeapFree( GetProcessHeap(), 0, pszList) ;
431     }
432
433 /*************************************************************************
434  * RunFileDlgA                                  [internal]
435  *
436  * The ANSI function that is available as ordinal 61 on Windows 9x/Me
437  *
438  * SEE ALSO
439  *   RunFileDlgAW
440  */
441 static void RunFileDlgA(
442         HWND hwndOwner,
443         HICON hIcon,
444         LPCSTR lpstrDirectory,
445         LPCSTR lpstrTitle,
446         LPCSTR lpstrDescription,
447         UINT uFlags)
448 {
449     WCHAR title[MAX_PATH];       /* longer string wouldn't be visible in the dialog anyway */
450     WCHAR description[MAX_PATH];
451     WCHAR directory[MAX_PATH];
452
453     MultiByteToWideChar(CP_ACP, 0, lpstrTitle, -1, title, MAX_PATH);
454     title[MAX_PATH - 1] = 0;
455     MultiByteToWideChar(CP_ACP, 0, lpstrDescription, -1, description, MAX_PATH);
456     description[MAX_PATH - 1] = 0;
457     if (!MultiByteToWideChar(CP_ACP, 0, lpstrDirectory, -1, directory, MAX_PATH))
458         directory[0] = 0;
459
460     RunFileDlgW(hwndOwner, hIcon,
461         lpstrDirectory ? directory : NULL,
462         lpstrTitle ? title : NULL,
463         lpstrDescription ? description : NULL,
464         uFlags);
465 }
466
467 /*************************************************************************
468  * RunFileDlgAW                                 [SHELL32.61]
469  *
470  * An undocumented way to open the Run File dialog. A documented way is to use
471  * CLSID_Shell, IID_IShellDispatch (as of Wine 1.0, not implemented under Wine)
472  *
473  * Exported by ordinal. ANSI on Windows 9x and Unicode on Windows NT/2000/XP/etc
474  *
475  */
476 void WINAPI RunFileDlgAW(
477         HWND hwndOwner,
478         HICON hIcon,
479         LPCVOID lpstrDirectory,
480         LPCVOID lpstrTitle,
481         LPCVOID lpstrDescription,
482         UINT uFlags)
483 {
484     if (SHELL_OsIsUnicode())
485         RunFileDlgW(hwndOwner, hIcon, lpstrDirectory, lpstrTitle, lpstrDescription, uFlags);
486     else
487         RunFileDlgA(hwndOwner, hIcon, lpstrDirectory, lpstrTitle, lpstrDescription, uFlags);
488 }
489
490
491 /*************************************************************************
492  * ConfirmDialog                                [internal]
493  *
494  * Put up a confirm box, return TRUE if the user confirmed
495  */
496 static BOOL ConfirmDialog(HWND hWndOwner, UINT PromptId, UINT TitleId)
497 {
498   WCHAR Prompt[256];
499   WCHAR Title[256];
500
501   LoadStringW(shell32_hInstance, PromptId, Prompt, sizeof(Prompt) / sizeof(WCHAR));
502   LoadStringW(shell32_hInstance, TitleId, Title, sizeof(Title) / sizeof(WCHAR));
503   return MessageBoxW(hWndOwner, Prompt, Title, MB_YESNO|MB_ICONQUESTION) == IDYES;
504 }
505
506
507 /*************************************************************************
508  * RestartDialogEx                              [SHELL32.730]
509  */
510
511 int WINAPI RestartDialogEx(HWND hWndOwner, LPCWSTR lpwstrReason, DWORD uFlags, DWORD uReason)
512 {
513     TRACE("(%p)\n", hWndOwner);
514
515     /* FIXME: use lpwstrReason */
516     if (ConfirmDialog(hWndOwner, IDS_RESTART_PROMPT, IDS_RESTART_TITLE))
517     {
518         HANDLE hToken;
519         TOKEN_PRIVILEGES npr;
520
521         /* enable the shutdown privilege for the current process */
522         if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
523         {
524             LookupPrivilegeValueA(0, "SeShutdownPrivilege", &npr.Privileges[0].Luid);
525             npr.PrivilegeCount = 1;
526             npr.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
527             AdjustTokenPrivileges(hToken, FALSE, &npr, 0, 0, 0);
528             CloseHandle(hToken);
529         }
530         ExitWindowsEx(EWX_REBOOT, uReason);
531     }
532
533     return 0;
534 }
535
536
537 /*************************************************************************
538  * RestartDialog                                [SHELL32.59]
539  */
540
541 int WINAPI RestartDialog(HWND hWndOwner, LPCWSTR lpstrReason, DWORD uFlags)
542 {
543     return RestartDialogEx(hWndOwner, lpstrReason, uFlags, 0);
544 }
545
546
547 /*************************************************************************
548  * ExitWindowsDialog                            [SHELL32.60]
549  *
550  * NOTES
551  *     exported by ordinal
552  */
553 void WINAPI ExitWindowsDialog (HWND hWndOwner)
554 {
555     TRACE("(%p)\n", hWndOwner);
556
557     if (ConfirmDialog(hWndOwner, IDS_SHUTDOWN_PROMPT, IDS_SHUTDOWN_TITLE))
558     {
559         HANDLE hToken;
560         TOKEN_PRIVILEGES npr;
561
562         /* enable shutdown privilege for current process */
563         if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
564         {
565             LookupPrivilegeValueA(0, "SeShutdownPrivilege", &npr.Privileges[0].Luid);
566             npr.PrivilegeCount = 1;
567             npr.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
568             AdjustTokenPrivileges(hToken, FALSE, &npr, 0, 0, 0);
569             CloseHandle(hToken);
570         }
571         ExitWindowsEx(EWX_SHUTDOWN, 0);
572     }
573 }