shell32: Make some data static const.
[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 BOOL 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 void WINAPI 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                         LPCWSTR working_dir ;
215                         psz = HeapAlloc( GetProcessHeap(), 0, (ic + 1)*sizeof(WCHAR) );
216                         GetWindowTextW (htxt, psz, ic + 1) ;
217
218                         /* according to http://www.codeproject.com/KB/shell/runfiledlg.aspx we should send a
219                          * WM_NOTIFY before execution */
220
221                         if (prfdp->lpstrDirectory)
222                             working_dir = prfdp->lpstrDirectory;
223                         else
224                             working_dir = parent = RunDlg_GetParentDir(psz);
225
226                         if (ShellExecuteW(hwnd, NULL, psz, NULL, working_dir, SW_SHOWNORMAL) < (HINSTANCE)33)
227                             {
228                             char *pszSysMsg = NULL ;
229                             char szMsg[256];
230                             FormatMessageA (
231                                 FORMAT_MESSAGE_ALLOCATE_BUFFER |
232                                 FORMAT_MESSAGE_FROM_SYSTEM |
233                                 FORMAT_MESSAGE_IGNORE_INSERTS,
234                                 NULL, GetLastError (),
235                                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
236                                 (LPSTR)&pszSysMsg, 0, NULL
237                                 ) ;
238                             sprintf (szMsg, "Error: %s", pszSysMsg) ;
239                             LocalFree ((HLOCAL)pszSysMsg) ;
240                             MessageBoxA (hwnd, szMsg, "Nix", MB_OK | MB_ICONEXCLAMATION) ;
241
242                             HeapFree(GetProcessHeap(), 0, psz);
243                             SendMessageA (htxt, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
244                             return TRUE ;
245                             }
246
247                         /* FillList is still ANSI */
248                         GetWindowTextA (htxt, (LPSTR)psz, ic + 1) ;
249                         FillList (htxt, (LPSTR)psz, FALSE) ;
250
251                         HeapFree(GetProcessHeap(), 0, psz);
252                         HeapFree(GetProcessHeap(), 0, parent);
253                         EndDialog (hwnd, 0) ;
254                         }
255                     }
256
257                 case IDCANCEL :
258                     EndDialog (hwnd, 0) ;
259                     return TRUE ;
260
261                 case IDC_RUNDLG_BROWSE :
262                     {
263                     HMODULE hComdlg = NULL ;
264                     LPFNOFN ofnProc = NULL ;
265                     static const WCHAR comdlg32W[] = {'c','o','m','d','l','g','3','2',0};
266                     WCHAR szFName[1024] = {0};
267                     WCHAR *pszFilter, szCaption[MAX_PATH];
268                     OPENFILENAMEW ofn;
269
270                     LoadStringW(shell32_hInstance, IDS_RUNDLG_BROWSE_FILTER, (LPWSTR)&pszFilter, 0);
271                     LoadStringW(shell32_hInstance, IDS_RUNDLG_BROWSE_CAPTION, szCaption, MAX_PATH);
272
273                     ZeroMemory(&ofn, sizeof(ofn));
274                     ofn.lStructSize = sizeof(OPENFILENAMEW);
275                     ofn.hwndOwner = hwnd;
276                     ofn.lpstrFilter = pszFilter;
277                     ofn.lpstrFile = szFName;
278                     ofn.nMaxFile = 1023;
279                     ofn.lpstrTitle = szCaption;
280                     ofn.Flags = OFN_ENABLESIZING | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
281                     ofn.lpstrInitialDir = prfdp->lpstrDirectory;
282
283                     if (NULL == (hComdlg = LoadLibraryExW (comdlg32W, NULL, 0)) ||
284                         NULL == (ofnProc = (LPFNOFN)GetProcAddress (hComdlg, "GetOpenFileNameW")))
285                     {
286                         ERR("Couldn't get GetOpenFileName function entry (lib=%p, proc=%p)\n", hComdlg, ofnProc);
287                         ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_RUNDLG_BROWSE_ERROR), NULL, MB_OK | MB_ICONERROR);
288                         return TRUE ;
289                     }
290
291                     if (ofnProc(&ofn))
292                     {
293                         SetFocus (GetDlgItem (hwnd, IDOK)) ;
294                         SetWindowTextW (GetDlgItem (hwnd, IDC_RUNDLG_EDITPATH), szFName) ;
295                         SendMessageW (GetDlgItem (hwnd, IDC_RUNDLG_EDITPATH), CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
296                         SetFocus (GetDlgItem (hwnd, IDOK)) ;
297                     }
298
299                     FreeLibrary (hComdlg) ;
300
301                     return TRUE ;
302                     }
303                 }
304             return TRUE ;
305         }
306     return FALSE ;
307     }
308
309 /* This grabs the MRU list from the registry and fills the combo for the "Run" dialog above */
310 /* fShowDefault ignored if pszLatest != NULL */
311 static void FillList (HWND hCb, char *pszLatest, BOOL fShowDefault)
312     {
313     HKEY hkey ;
314 /*    char szDbgMsg[256] = "" ; */
315     char *pszList = NULL, *pszCmd = NULL, cMatch = 0, cMax = 0x60, szIndex[2] = "-" ;
316     DWORD icList = 0, icCmd = 0 ;
317     UINT Nix ;
318
319     SendMessageA (hCb, CB_RESETCONTENT, 0, 0) ;
320
321     if (ERROR_SUCCESS != RegCreateKeyExA (
322         HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU",
323         0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL))
324         MessageBoxA (hCb, "Unable to open registry key !", "Nix", MB_OK) ;
325
326     RegQueryValueExA (hkey, "MRUList", NULL, NULL, NULL, &icList) ;
327
328     if (icList > 0)
329         {
330         pszList = HeapAlloc( GetProcessHeap(), 0, icList) ;
331         if (ERROR_SUCCESS != RegQueryValueExA (hkey, "MRUList", NULL, NULL, (LPBYTE)pszList, &icList))
332             MessageBoxA (hCb, "Unable to grab MRUList !", "Nix", MB_OK) ;
333         }
334     else
335         {
336         icList = 1 ;
337         pszList = HeapAlloc( GetProcessHeap(), 0, icList) ;
338         pszList[0] = 0 ;
339         }
340
341     for (Nix = 0 ; Nix < icList - 1 ; Nix++)
342         {
343         if (pszList[Nix] > cMax)
344             cMax = pszList[Nix] ;
345
346         szIndex[0] = pszList[Nix] ;
347
348         if (ERROR_SUCCESS != RegQueryValueExA (hkey, szIndex, NULL, NULL, NULL, &icCmd))
349             MessageBoxA (hCb, "Unable to grab size of index", "Nix", MB_OK) ;
350         if( pszCmd )
351             pszCmd = HeapReAlloc(GetProcessHeap(), 0, pszCmd, icCmd) ;
352         else
353             pszCmd = HeapAlloc(GetProcessHeap(), 0, icCmd) ;
354         if (ERROR_SUCCESS != RegQueryValueExA (hkey, szIndex, NULL, NULL, (LPBYTE)pszCmd, &icCmd))
355             MessageBoxA (hCb, "Unable to grab index", "Nix", MB_OK) ;
356
357         if (NULL != pszLatest)
358             {
359             if (!lstrcmpiA(pszCmd, pszLatest))
360                 {
361                 /*
362                 sprintf (szDbgMsg, "Found existing (%d).\n", Nix) ;
363                 MessageBoxA (hCb, szDbgMsg, "Nix", MB_OK) ;
364                 */
365                 SendMessageA (hCb, CB_INSERTSTRING, 0, (LPARAM)pszCmd) ;
366                 SetWindowTextA (hCb, pszCmd) ;
367                 SendMessageA (hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
368
369                 cMatch = pszList[Nix] ;
370                 memmove (&pszList[1], pszList, Nix) ;
371                 pszList[0] = cMatch ;
372                 continue ;
373                 }
374             }
375
376         if (26 != icList - 1 || icList - 2 != Nix || cMatch || NULL == pszLatest)
377             {
378             /*
379             sprintf (szDbgMsg, "Happily appending (%d).\n", Nix) ;
380             MessageBoxA (hCb, szDbgMsg, "Nix", MB_OK) ;
381             */
382             SendMessageA (hCb, CB_ADDSTRING, 0, (LPARAM)pszCmd) ;
383             if (!Nix && fShowDefault)
384                 {
385                 SetWindowTextA (hCb, pszCmd) ;
386                 SendMessageA (hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
387                 }
388
389             }
390         else
391             {
392             /*
393             sprintf (szDbgMsg, "Doing loop thing.\n") ;
394             MessageBoxA (hCb, szDbgMsg, "Nix", MB_OK) ;
395             */
396             SendMessageA (hCb, CB_INSERTSTRING, 0, (LPARAM)pszLatest) ;
397             SetWindowTextA (hCb, pszLatest) ;
398             SendMessageA (hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
399
400             cMatch = pszList[Nix] ;
401             memmove (&pszList[1], pszList, Nix) ;
402             pszList[0] = cMatch ;
403             szIndex[0] = cMatch ;
404             RegSetValueExA (hkey, szIndex, 0, REG_SZ, (LPBYTE)pszLatest, strlen (pszLatest) + 1) ;
405             }
406         }
407
408     if (!cMatch && NULL != pszLatest)
409         {
410         /*
411         sprintf (szDbgMsg, "Simply inserting (increasing list).\n") ;
412         MessageBoxA (hCb, szDbgMsg, "Nix", MB_OK) ;
413         */
414         SendMessageA (hCb, CB_INSERTSTRING, 0, (LPARAM)pszLatest) ;
415         SetWindowTextA (hCb, pszLatest) ;
416         SendMessageA (hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
417
418         cMatch = ++cMax ;
419         if( pszList )
420             pszList = HeapReAlloc(GetProcessHeap(), 0, pszList, ++icList) ;
421         else
422             pszList = HeapAlloc(GetProcessHeap(), 0, ++icList) ;
423         memmove (&pszList[1], pszList, icList - 1) ;
424         pszList[0] = cMatch ;
425         szIndex[0] = cMatch ;
426         RegSetValueExA (hkey, szIndex, 0, REG_SZ, (LPBYTE)pszLatest, strlen (pszLatest) + 1) ;
427         }
428
429     RegSetValueExA (hkey, "MRUList", 0, REG_SZ, (LPBYTE)pszList, strlen (pszList) + 1) ;
430
431     HeapFree( GetProcessHeap(), 0, pszCmd) ;
432     HeapFree( GetProcessHeap(), 0, pszList) ;
433     }
434
435 /*************************************************************************
436  * RunFileDlgA                                  [internal]
437  *
438  * The ANSI function that is available as ordinal 61 on Windows 9x/Me
439  *
440  * SEE ALSO
441  *   RunFileDlgAW
442  */
443 void WINAPI RunFileDlgA(
444         HWND hwndOwner,
445         HICON hIcon,
446         LPCSTR lpstrDirectory,
447         LPCSTR lpstrTitle,
448         LPCSTR lpstrDescription,
449         UINT uFlags)
450 {
451     WCHAR title[MAX_PATH];       /* longer string wouldn't be visible in the dialog anyway */
452     WCHAR description[MAX_PATH];
453     WCHAR directory[MAX_PATH];
454
455     MultiByteToWideChar(CP_ACP, 0, lpstrTitle, -1, title, MAX_PATH);
456     title[MAX_PATH - 1] = 0;
457     MultiByteToWideChar(CP_ACP, 0, lpstrDescription, -1, description, MAX_PATH);
458     description[MAX_PATH - 1] = 0;
459     if (!MultiByteToWideChar(CP_ACP, 0, lpstrDirectory, -1, directory, MAX_PATH))
460         directory[0] = 0;
461
462     RunFileDlgW(hwndOwner, hIcon,
463         lpstrDirectory ? directory : NULL,
464         lpstrTitle ? title : NULL,
465         lpstrDescription ? description : NULL,
466         uFlags);
467 }
468
469 /*************************************************************************
470  * RunFileDlgAW                                 [SHELL32.61]
471  *
472  * An undocumented way to open the Run File dialog. A documented way is to use
473  * CLSID_Shell, IID_IShellDispatch (as of Wine 1.0, not implemented under Wine)
474  *
475  * Exported by ordinal. ANSI on Windows 9x and Unicode on Windows NT/2000/XP/etc
476  *
477  */
478 void WINAPI RunFileDlgAW(
479         HWND hwndOwner,
480         HICON hIcon,
481         LPCVOID lpstrDirectory,
482         LPCVOID lpstrTitle,
483         LPCVOID lpstrDescription,
484         UINT uFlags)
485 {
486     if (SHELL_OsIsUnicode())
487         RunFileDlgW(hwndOwner, hIcon, lpstrDirectory, lpstrTitle, lpstrDescription, uFlags);
488     else
489         RunFileDlgA(hwndOwner, hIcon, lpstrDirectory, lpstrTitle, lpstrDescription, uFlags);
490 }
491
492
493 /*************************************************************************
494  * ConfirmDialog                                [internal]
495  *
496  * Put up a confirm box, return TRUE if the user confirmed
497  */
498 static BOOL ConfirmDialog(HWND hWndOwner, UINT PromptId, UINT TitleId)
499 {
500   WCHAR Prompt[256];
501   WCHAR Title[256];
502
503   LoadStringW(shell32_hInstance, PromptId, Prompt, sizeof(Prompt) / sizeof(WCHAR));
504   LoadStringW(shell32_hInstance, TitleId, Title, sizeof(Title) / sizeof(WCHAR));
505   return MessageBoxW(hWndOwner, Prompt, Title, MB_YESNO|MB_ICONQUESTION) == IDYES;
506 }
507
508
509 /*************************************************************************
510  * RestartDialogEx                              [SHELL32.730]
511  */
512
513 int WINAPI RestartDialogEx(HWND hWndOwner, LPCWSTR lpwstrReason, DWORD uFlags, DWORD uReason)
514 {
515     TRACE("(%p)\n", hWndOwner);
516
517     /* FIXME: use lpwstrReason */
518     if (ConfirmDialog(hWndOwner, IDS_RESTART_PROMPT, IDS_RESTART_TITLE))
519     {
520         HANDLE hToken;
521         TOKEN_PRIVILEGES npr;
522
523         /* enable the shutdown privilege for the current process */
524         if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
525         {
526             LookupPrivilegeValueA(0, "SeShutdownPrivilege", &npr.Privileges[0].Luid);
527             npr.PrivilegeCount = 1;
528             npr.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
529             AdjustTokenPrivileges(hToken, FALSE, &npr, 0, 0, 0);
530             CloseHandle(hToken);
531         }
532         ExitWindowsEx(EWX_REBOOT, uReason);
533     }
534
535     return 0;
536 }
537
538
539 /*************************************************************************
540  * RestartDialog                                [SHELL32.59]
541  */
542
543 int WINAPI RestartDialog(HWND hWndOwner, LPCWSTR lpstrReason, DWORD uFlags)
544 {
545     return RestartDialogEx(hWndOwner, lpstrReason, uFlags, 0);
546 }
547
548
549 /*************************************************************************
550  * ExitWindowsDialog                            [SHELL32.60]
551  *
552  * NOTES
553  *     exported by ordinal
554  */
555 void WINAPI ExitWindowsDialog (HWND hWndOwner)
556 {
557     TRACE("(%p)\n", hWndOwner);
558
559     if (ConfirmDialog(hWndOwner, IDS_SHUTDOWN_PROMPT, IDS_SHUTDOWN_TITLE))
560     {
561         HANDLE hToken;
562         TOKEN_PRIVILEGES npr;
563
564         /* enable shutdown privilege for current process */
565         if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
566         {
567             LookupPrivilegeValueA(0, "SeShutdownPrivilege", &npr.Privileges[0].Luid);
568             npr.PrivilegeCount = 1;
569             npr.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
570             AdjustTokenPrivileges(hToken, FALSE, &npr, 0, 0, 0);
571             CloseHandle(hToken);
572         }
573         ExitWindowsEx(EWX_SHUTDOWN, 0);
574     }
575 }