1 /*
2 * common shell dialogs
3 *
4 * Copyright 2000 Juergen Schmied
5 * Copyright 2018 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6 * Copyright 2021 Arnav Bhatt <arnavbhatt288@gmail.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
23 #include "precomp.h"
24
25 typedef struct
26 {
27 HWND hwndOwner;
28 HICON hIcon;
29 LPCWSTR lpstrDirectory;
30 LPCWSTR lpstrTitle;
31 LPCWSTR lpstrDescription;
32 UINT uFlags;
33 BOOL bCoInited;
34 } RUNFILEDLGPARAMS;
35
36 typedef struct
37 {
38 BOOL bFriendlyUI;
39 BOOL bIsButtonHot[2];
40 HBITMAP hImageStrip;
41 HBRUSH hBrush;
42 HFONT hfFont;
43 WNDPROC OldButtonProc;
44 } LOGOFF_DLG_CONTEXT, *PLOGOFF_DLG_CONTEXT;
45
46 typedef BOOL (WINAPI * LPFNOFN) (OPENFILENAMEW *);
47
48 WINE_DEFAULT_DEBUG_CHANNEL(shell);
49 static INT_PTR CALLBACK RunDlgProc(HWND, UINT, WPARAM, LPARAM);
50 static void FillList(HWND, LPWSTR, UINT, BOOL);
51
52
53 /*************************************************************************
54 * PickIconDlg [SHELL32.62]
55 *
56 */
57
58 typedef struct
59 {
60 HMODULE hLibrary;
61 HWND hDlgCtrl;
62 WCHAR szPath[MAX_PATH];
63 INT Index;
64 INT nIcons;
65 HICON *phIcons;
66 } PICK_ICON_CONTEXT, *PPICK_ICON_CONTEXT;
67
EnumPickIconResourceProc(HMODULE hModule,LPCWSTR lpszType,LPWSTR lpszName,LONG_PTR lParam)68 BOOL CALLBACK EnumPickIconResourceProc(HMODULE hModule,
69 LPCWSTR lpszType,
70 LPWSTR lpszName,
71 LONG_PTR lParam)
72 {
73 PPICK_ICON_CONTEXT pIconContext = PPICK_ICON_CONTEXT(lParam);
74 HWND hDlgCtrl = pIconContext->hDlgCtrl;
75
76 if (IS_INTRESOURCE(lpszName))
77 lParam = LOWORD(lpszName);
78 else
79 lParam = -1;
80
81 SendMessageW(hDlgCtrl, LB_ADDSTRING, 0, lParam);
82
83 return TRUE;
84 }
85
86 static void
DestroyIconList(HWND hDlgCtrl,PPICK_ICON_CONTEXT pIconContext)87 DestroyIconList(HWND hDlgCtrl, PPICK_ICON_CONTEXT pIconContext)
88 {
89 int count;
90 int index;
91
92 count = SendMessageW(hDlgCtrl, LB_GETCOUNT, 0, 0);
93 if (count == LB_ERR)
94 return;
95
96 for(index = 0; index < count; index++)
97 {
98 DestroyIcon(pIconContext->phIcons[index]);
99 pIconContext->phIcons[index] = NULL;
100 }
101 }
102
103 static BOOL
DoLoadIcons(HWND hwndDlg,PPICK_ICON_CONTEXT pIconContext,LPCWSTR pszFile)104 DoLoadIcons(HWND hwndDlg, PPICK_ICON_CONTEXT pIconContext, LPCWSTR pszFile)
105 {
106 WCHAR szExpandedPath[MAX_PATH];
107
108 // Destroy previous icons
109 DestroyIconList(pIconContext->hDlgCtrl, pIconContext);
110 SendMessageW(pIconContext->hDlgCtrl, LB_RESETCONTENT, 0, 0);
111 delete[] pIconContext->phIcons;
112
113 // Store the path
114 StringCchCopyW(pIconContext->szPath, _countof(pIconContext->szPath), pszFile);
115 ExpandEnvironmentStringsW(pszFile, szExpandedPath, _countof(szExpandedPath));
116
117 // Load the module if possible
118 HMODULE hLibrary = LoadLibraryExW(szExpandedPath, NULL, LOAD_LIBRARY_AS_DATAFILE);
119 if (pIconContext->hLibrary)
120 FreeLibrary(pIconContext->hLibrary);
121 pIconContext->hLibrary = hLibrary;
122
123 if (pIconContext->hLibrary)
124 {
125 // Load the icons from the module
126 pIconContext->nIcons = ExtractIconExW(szExpandedPath, -1, NULL, NULL, 0);
127 pIconContext->phIcons = new HICON[pIconContext->nIcons];
128
129 if (ExtractIconExW(szExpandedPath, 0, pIconContext->phIcons, NULL, pIconContext->nIcons))
130 {
131 EnumResourceNamesW(pIconContext->hLibrary, RT_GROUP_ICON, EnumPickIconResourceProc, (LPARAM)pIconContext);
132 }
133 else
134 {
135 pIconContext->nIcons = 0;
136 }
137 }
138 else
139 {
140 // .ico file
141 pIconContext->nIcons = 1;
142 pIconContext->phIcons = new HICON[1];
143
144 if (ExtractIconExW(szExpandedPath, 0, pIconContext->phIcons, NULL, pIconContext->nIcons))
145 {
146 SendMessageW(pIconContext->hDlgCtrl, LB_ADDSTRING, 0, 0);
147 }
148 else
149 {
150 pIconContext->nIcons = 0;
151 }
152 }
153
154 // Set the text and reset the edit control's modification flag
155 SetDlgItemTextW(hwndDlg, IDC_EDIT_PATH, pIconContext->szPath);
156 SendDlgItemMessage(hwndDlg, IDC_EDIT_PATH, EM_SETMODIFY, FALSE, 0);
157
158 if (pIconContext->nIcons == 0)
159 {
160 delete[] pIconContext->phIcons;
161 pIconContext->phIcons = NULL;
162 }
163
164 return (pIconContext->nIcons > 0);
165 }
166
NoIconsInFile(HWND hwndDlg,PPICK_ICON_CONTEXT pIconContext)167 static void NoIconsInFile(HWND hwndDlg, PPICK_ICON_CONTEXT pIconContext)
168 {
169 // Show an error message
170 CStringW strText, strTitle(MAKEINTRESOURCEW(IDS_PICK_ICON_TITLE));
171 strText.Format(IDS_NO_ICONS, pIconContext->szPath);
172 MessageBoxW(hwndDlg, strText, strTitle, MB_ICONWARNING);
173
174 // Load the default icons
175 DoLoadIcons(hwndDlg, pIconContext, g_pszShell32);
176 }
177
178 // Icon size
179 #define CX_ICON GetSystemMetrics(SM_CXICON)
180 #define CY_ICON GetSystemMetrics(SM_CYICON)
181
182 // Item size
183 #define CX_ITEM (CX_ICON + 4)
184 #define CY_ITEM (CY_ICON + 12)
185
PickIconProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)186 INT_PTR CALLBACK PickIconProc(
187 HWND hwndDlg,
188 UINT uMsg,
189 WPARAM wParam,
190 LPARAM lParam)
191 {
192 LPMEASUREITEMSTRUCT lpmis;
193 LPDRAWITEMSTRUCT lpdis;
194 HICON hIcon;
195 INT index, count;
196 WCHAR szText[MAX_PATH], szFilter[100];
197 CStringW strTitle;
198 OPENFILENAMEW ofn;
199
200 PPICK_ICON_CONTEXT pIconContext = (PPICK_ICON_CONTEXT)GetWindowLongPtr(hwndDlg, DWLP_USER);
201
202 switch(uMsg)
203 {
204 case WM_INITDIALOG:
205 {
206 pIconContext = (PPICK_ICON_CONTEXT)lParam;
207 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pIconContext);
208 pIconContext->hDlgCtrl = GetDlgItem(hwndDlg, IDC_PICKICON_LIST);
209
210 SendMessageW(pIconContext->hDlgCtrl, LB_SETCOLUMNWIDTH, CX_ITEM, 0);
211
212 // Load the icons
213 if (!DoLoadIcons(hwndDlg, pIconContext, pIconContext->szPath))
214 NoIconsInFile(hwndDlg, pIconContext);
215
216 // Set the selection
217 count = SendMessageW(pIconContext->hDlgCtrl, LB_GETCOUNT, 0, 0);
218 if (count != LB_ERR)
219 {
220 if (pIconContext->Index < 0)
221 {
222 // A negative value will be interpreted as a negated resource ID.
223 LPARAM lParam = -pIconContext->Index;
224 pIconContext->Index = (INT)SendMessageW(pIconContext->hDlgCtrl, LB_FINDSTRINGEXACT, -1, lParam);
225 }
226
227 if (pIconContext->Index < 0 || count <= pIconContext->Index)
228 pIconContext->Index = 0;
229
230 SendMessageW(pIconContext->hDlgCtrl, LB_SETCURSEL, pIconContext->Index, 0);
231 SendMessageW(pIconContext->hDlgCtrl, LB_SETTOPINDEX, pIconContext->Index, 0);
232 }
233
234 SHAutoComplete(GetDlgItem(hwndDlg, IDC_EDIT_PATH), SHACF_DEFAULT);
235 return TRUE;
236 }
237
238 case WM_DESTROY:
239 {
240 DestroyIconList(pIconContext->hDlgCtrl, pIconContext);
241 delete[] pIconContext->phIcons;
242
243 if (pIconContext->hLibrary)
244 FreeLibrary(pIconContext->hLibrary);
245 break;
246 }
247
248 case WM_COMMAND:
249 switch(LOWORD(wParam))
250 {
251 case IDOK:
252 {
253 /* Check whether the path edit control has been modified; if so load the icons instead of validating */
254 if (SendDlgItemMessage(hwndDlg, IDC_EDIT_PATH, EM_GETMODIFY, 0, 0))
255 {
256 /* Reset the edit control's modification flag and retrieve the text */
257 SendDlgItemMessage(hwndDlg, IDC_EDIT_PATH, EM_SETMODIFY, FALSE, 0);
258 GetDlgItemTextW(hwndDlg, IDC_EDIT_PATH, szText, _countof(szText));
259
260 // Load the icons
261 if (!DoLoadIcons(hwndDlg, pIconContext, szText))
262 NoIconsInFile(hwndDlg, pIconContext);
263
264 // Set the selection
265 SendMessageW(pIconContext->hDlgCtrl, LB_SETCURSEL, 0, 0);
266 break;
267 }
268
269 /* The path edit control has not been modified, return the selection */
270 pIconContext->Index = (INT)SendMessageW(pIconContext->hDlgCtrl, LB_GETCURSEL, 0, 0);
271 GetDlgItemTextW(hwndDlg, IDC_EDIT_PATH, pIconContext->szPath, _countof(pIconContext->szPath));
272 EndDialog(hwndDlg, 1);
273 break;
274 }
275
276 case IDCANCEL:
277 EndDialog(hwndDlg, 0);
278 break;
279
280 case IDC_PICKICON_LIST:
281 switch (HIWORD(wParam))
282 {
283 case LBN_SELCHANGE:
284 InvalidateRect((HWND)lParam, NULL, TRUE);
285 break;
286
287 case LBN_DBLCLK:
288 SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDOK, 0), 0);
289 break;
290 }
291 break;
292
293 case IDC_BUTTON_PATH:
294 {
295 // Choose the module path
296 szText[0] = 0;
297 szFilter[0] = 0;
298 ZeroMemory(&ofn, sizeof(ofn));
299 ofn.lStructSize = sizeof(ofn);
300 ofn.hwndOwner = hwndDlg;
301 ofn.lpstrFile = szText;
302 ofn.nMaxFile = _countof(szText);
303 strTitle.LoadString(IDS_PICK_ICON_TITLE);
304 ofn.lpstrTitle = strTitle;
305 LoadStringW(shell32_hInstance, IDS_PICK_ICON_FILTER, szFilter, _countof(szFilter));
306 ofn.lpstrFilter = szFilter;
307 if (!GetOpenFileNameW(&ofn))
308 break;
309
310 // Load the icons
311 if (!DoLoadIcons(hwndDlg, pIconContext, szText))
312 NoIconsInFile(hwndDlg, pIconContext);
313
314 // Set the selection
315 SendMessageW(pIconContext->hDlgCtrl, LB_SETCURSEL, 0, 0);
316 break;
317 }
318
319 default:
320 break;
321 }
322 break;
323
324 case WM_MEASUREITEM:
325 lpmis = (LPMEASUREITEMSTRUCT)lParam;
326 lpmis->itemHeight = CY_ITEM;
327 return TRUE;
328
329 case WM_DRAWITEM:
330 {
331 lpdis = (LPDRAWITEMSTRUCT)lParam;
332 if (lpdis->itemID == (UINT)-1)
333 break;
334 switch (lpdis->itemAction)
335 {
336 case ODA_SELECT:
337 case ODA_DRAWENTIRE:
338 {
339 index = SendMessageW(pIconContext->hDlgCtrl, LB_GETCURSEL, 0, 0);
340 hIcon = pIconContext->phIcons[lpdis->itemID];
341
342 if (lpdis->itemID == (UINT)index)
343 FillRect(lpdis->hDC, &lpdis->rcItem, (HBRUSH)(COLOR_HIGHLIGHT + 1));
344 else
345 FillRect(lpdis->hDC, &lpdis->rcItem, (HBRUSH)(COLOR_WINDOW + 1));
346
347 // Centering
348 INT x = lpdis->rcItem.left + (CX_ITEM - CX_ICON) / 2;
349 INT y = lpdis->rcItem.top + (CY_ITEM - CY_ICON) / 2;
350
351 DrawIconEx(lpdis->hDC, x, y, hIcon, 0, 0, 0, NULL, DI_NORMAL);
352 break;
353 }
354 }
355 return TRUE;
356 }
357 }
358
359 return FALSE;
360 }
361
PickIconDlg(HWND hWndOwner,LPWSTR lpstrFile,UINT nMaxFile,INT * lpdwIconIndex)362 BOOL WINAPI PickIconDlg(
363 HWND hWndOwner,
364 LPWSTR lpstrFile,
365 UINT nMaxFile,
366 INT* lpdwIconIndex)
367 {
368 int res;
369 WCHAR szExpandedPath[MAX_PATH];
370
371 // Initialize the dialog
372 PICK_ICON_CONTEXT IconContext = { NULL };
373 IconContext.Index = *lpdwIconIndex;
374 StringCchCopyW(IconContext.szPath, _countof(IconContext.szPath), lpstrFile);
375 ExpandEnvironmentStringsW(lpstrFile, szExpandedPath, _countof(szExpandedPath));
376
377 if (!szExpandedPath[0] ||
378 GetFileAttributesW(szExpandedPath) == INVALID_FILE_ATTRIBUTES)
379 {
380 if (szExpandedPath[0])
381 {
382 // No such file
383 CStringW strText, strTitle(MAKEINTRESOURCEW(IDS_PICK_ICON_TITLE));
384 strText.Format(IDS_FILE_NOT_FOUND, lpstrFile);
385 MessageBoxW(hWndOwner, strText, strTitle, MB_ICONWARNING);
386 }
387
388 // Set the default value
389 StringCchCopyW(IconContext.szPath, _countof(IconContext.szPath), g_pszShell32);
390 }
391
392 // Show the dialog
393 res = DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_PICK_ICON), hWndOwner, PickIconProc, (LPARAM)&IconContext);
394 if (res)
395 {
396 // Store the selected icon
397 StringCchCopyW(lpstrFile, nMaxFile, IconContext.szPath);
398 *lpdwIconIndex = IconContext.Index;
399 }
400
401 return res;
402 }
403
404 /*************************************************************************
405 * RunFileDlg [internal]
406 *
407 * The Unicode function that is available as ordinal 61 on Windows NT/2000/XP/...
408 */
RunFileDlg(HWND hWndOwner,HICON hIcon,LPCWSTR lpstrDirectory,LPCWSTR lpstrTitle,LPCWSTR lpstrDescription,UINT uFlags)409 void WINAPI RunFileDlg(
410 HWND hWndOwner,
411 HICON hIcon,
412 LPCWSTR lpstrDirectory,
413 LPCWSTR lpstrTitle,
414 LPCWSTR lpstrDescription,
415 UINT uFlags)
416 {
417 TRACE("\n");
418
419 RUNFILEDLGPARAMS rfdp;
420 rfdp.hwndOwner = hWndOwner;
421 rfdp.hIcon = hIcon;
422 rfdp.lpstrDirectory = lpstrDirectory;
423 rfdp.lpstrTitle = lpstrTitle;
424 rfdp.lpstrDescription = lpstrDescription;
425 rfdp.uFlags = uFlags;
426
427 DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_RUN), hWndOwner, RunDlgProc, (LPARAM)&rfdp);
428 }
429
430
431 /* find the directory that contains the file being run */
RunDlg_GetParentDir(LPCWSTR cmdline)432 static LPWSTR RunDlg_GetParentDir(LPCWSTR cmdline)
433 {
434 const WCHAR *src;
435 WCHAR *dest, *result, *result_end=NULL;
436
437 result = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(strlenW(cmdline)+5));
438
439 if (NULL == result)
440 {
441 TRACE("HeapAlloc couldn't allocate %d bytes\n", sizeof(WCHAR)*(strlenW(cmdline)+5));
442 return NULL;
443 }
444
445 src = cmdline;
446 dest = result;
447
448 if (*src == '"')
449 {
450 src++;
451 while (*src && *src != '"')
452 {
453 if (*src == '\\')
454 result_end = dest;
455 *dest++ = *src++;
456 }
457 }
458 else {
459 while (*src)
460 {
461 if (isspaceW(*src))
462 {
463 *dest = 0;
464 if (INVALID_FILE_ATTRIBUTES != GetFileAttributesW(result))
465 break;
466 strcatW(dest, L".exe");
467 if (INVALID_FILE_ATTRIBUTES != GetFileAttributesW(result))
468 break;
469 }
470 else if (*src == '\\')
471 result_end = dest;
472 *dest++ = *src++;
473 }
474 }
475
476 if (result_end)
477 {
478 *result_end = 0;
479 return result;
480 }
481 else
482 {
483 HeapFree(GetProcessHeap(), 0, result);
484 return NULL;
485 }
486 }
487
EnableOkButtonFromEditContents(HWND hwnd)488 static void EnableOkButtonFromEditContents(HWND hwnd)
489 {
490 BOOL Enable = FALSE;
491 INT Length, n;
492 HWND Edit = GetDlgItem(hwnd, IDC_RUNDLG_EDITPATH);
493 Length = GetWindowTextLengthW(Edit);
494 if (Length > 0)
495 {
496 PWCHAR psz = (PWCHAR)HeapAlloc(GetProcessHeap(), 0, (Length + 1) * sizeof(WCHAR));
497 if (psz)
498 {
499 GetWindowTextW(Edit, psz, Length + 1);
500 for (n = 0; n < Length && !Enable; ++n)
501 Enable = psz[n] != ' ';
502 HeapFree(GetProcessHeap(), 0, psz);
503 }
504 else
505 Enable = TRUE;
506 }
507 EnableWindow(GetDlgItem(hwnd, IDOK), Enable);
508 }
509
510 /* Dialog procedure for RunFileDlg */
RunDlgProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)511 static INT_PTR CALLBACK RunDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
512 {
513 RUNFILEDLGPARAMS *prfdp = (RUNFILEDLGPARAMS *)GetWindowLongPtrW(hwnd, DWLP_USER);
514 HWND hwndCombo, hwndEdit;
515 COMBOBOXINFO ComboInfo;
516
517 switch (message)
518 {
519 case WM_INITDIALOG:
520 prfdp = (RUNFILEDLGPARAMS *)lParam;
521 SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR)prfdp);
522
523 if (prfdp->lpstrTitle)
524 SetWindowTextW(hwnd, prfdp->lpstrTitle);
525 if (prfdp->lpstrDescription)
526 SetWindowTextW(GetDlgItem(hwnd, IDC_RUNDLG_DESCRIPTION), prfdp->lpstrDescription);
527 if (prfdp->uFlags & RFF_NOBROWSE)
528 {
529 HWND browse = GetDlgItem(hwnd, IDC_RUNDLG_BROWSE);
530 ShowWindow(browse, SW_HIDE);
531 EnableWindow(browse, FALSE);
532 }
533 if (prfdp->uFlags & RFF_NOLABEL)
534 ShowWindow(GetDlgItem(hwnd, IDC_RUNDLG_LABEL), SW_HIDE);
535 if (prfdp->uFlags & RFF_NOSEPARATEMEM)
536 {
537 FIXME("RFF_NOSEPARATEMEM not supported\n");
538 }
539
540 /* Use the default Shell Run icon if no one is specified */
541 if (prfdp->hIcon == NULL)
542 prfdp->hIcon = LoadIconW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_RUN));
543 /*
544 * NOTE: Starting Windows Vista, the "Run File" dialog gets a
545 * title icon that remains the same as the default one, even if
546 * the user specifies a custom icon.
547 * Since we currently imitate Windows 2003, therefore do not show
548 * any title icon.
549 */
550 // SendMessageW(hwnd, WM_SETICON, ICON_BIG, (LPARAM)prfdp->hIcon);
551 // SendMessageW(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)prfdp->hIcon);
552 SendMessageW(GetDlgItem(hwnd, IDC_RUNDLG_ICON), STM_SETICON, (WPARAM)prfdp->hIcon, 0);
553
554 hwndCombo = GetDlgItem(hwnd, IDC_RUNDLG_EDITPATH);
555 FillList(hwndCombo, NULL, 0, (prfdp->uFlags & RFF_NODEFAULT) == 0);
556 EnableOkButtonFromEditContents(hwnd);
557
558 ComboInfo.cbSize = sizeof(ComboInfo);
559 GetComboBoxInfo(hwndCombo, &ComboInfo);
560 hwndEdit = ComboInfo.hwndItem;
561 ASSERT(::IsWindow(hwndEdit));
562
563 // SHAutoComplete needs co init
564 prfdp->bCoInited = SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
565
566 SHAutoComplete(hwndEdit, SHACF_FILESYSTEM | SHACF_FILESYS_ONLY | SHACF_URLALL);
567
568 SetFocus(hwndCombo);
569 return TRUE;
570
571 case WM_DESTROY:
572 if (prfdp->bCoInited)
573 CoUninitialize();
574 break;
575
576 case WM_COMMAND:
577 switch (LOWORD(wParam))
578 {
579 case IDOK:
580 {
581 LRESULT lRet;
582 HWND htxt = GetDlgItem(hwnd, IDC_RUNDLG_EDITPATH);
583 INT ic;
584 WCHAR *psz, *pszExpanded, *parent = NULL;
585 DWORD cchExpand;
586 SHELLEXECUTEINFOW sei;
587 NMRUNFILEDLGW nmrfd;
588
589 ic = GetWindowTextLengthW(htxt);
590 if (ic == 0)
591 {
592 EndDialog(hwnd, IDCANCEL);
593 return TRUE;
594 }
595
596 ZeroMemory(&sei, sizeof(sei));
597 sei.cbSize = sizeof(sei);
598
599 /*
600 * Allocate a new MRU entry, we need to add two characters
601 * for the terminating "\\1" part, then the NULL character.
602 */
603 psz = (WCHAR*)HeapAlloc(GetProcessHeap(), 0, (ic + 2 + 1)*sizeof(WCHAR));
604 if (!psz)
605 {
606 EndDialog(hwnd, IDCANCEL);
607 return TRUE;
608 }
609
610 GetWindowTextW(htxt, psz, ic + 1);
611 sei.hwnd = hwnd;
612 sei.nShow = SW_SHOWNORMAL;
613 sei.lpFile = psz;
614 StrTrimW(psz, L" \t");
615
616 if (wcschr(psz, L'%') != NULL)
617 {
618 cchExpand = ExpandEnvironmentStringsW(psz, NULL, 0);
619 pszExpanded = (WCHAR*)HeapAlloc(GetProcessHeap(), 0, cchExpand * sizeof(WCHAR));
620 if (!pszExpanded)
621 {
622 HeapFree(GetProcessHeap(), 0, psz);
623 EndDialog(hwnd, IDCANCEL);
624 return TRUE;
625 }
626 ExpandEnvironmentStringsW(psz, pszExpanded, cchExpand);
627 StrTrimW(pszExpanded, L" \t");
628 }
629 else
630 {
631 pszExpanded = psz;
632 }
633
634 /*
635 * The precedence is the following: first the user-given
636 * current directory is used; if there is none, a current
637 * directory is computed if the RFF_CALCDIRECTORY is set,
638 * otherwise no current directory is defined.
639 */
640 LPCWSTR pszStartDir;
641 if (prfdp->lpstrDirectory)
642 {
643 sei.lpDirectory = prfdp->lpstrDirectory;
644 pszStartDir = prfdp->lpstrDirectory;
645 }
646 else if (prfdp->uFlags & RFF_CALCDIRECTORY)
647 {
648 sei.lpDirectory = parent = RunDlg_GetParentDir(sei.lpFile);
649 pszStartDir = parent = RunDlg_GetParentDir(pszExpanded);
650 }
651 else
652 {
653 sei.lpDirectory = NULL;
654 pszStartDir = NULL;
655 }
656
657 /* Hide the dialog for now on, we will show it up in case of retry */
658 ShowWindow(hwnd, SW_HIDE);
659
660 /*
661 * As shown by manual tests on Windows, modifying the contents
662 * of the notification structure will not modify what the
663 * Run-Dialog will use for the nShow parameter. However the
664 * lpFile and lpDirectory pointers are set to the buffers used
665 * by the Run-Dialog, as a consequence they can be modified by
666 * the notification receiver, as long as it respects the lengths
667 * of the buffers (to avoid buffer overflows).
668 */
669 nmrfd.hdr.code = RFN_VALIDATE;
670 nmrfd.hdr.hwndFrom = hwnd;
671 nmrfd.hdr.idFrom = 0;
672 nmrfd.lpFile = pszExpanded;
673 nmrfd.lpDirectory = pszStartDir;
674 nmrfd.nShow = SW_SHOWNORMAL;
675
676 lRet = SendMessageW(prfdp->hwndOwner, WM_NOTIFY, 0, (LPARAM)&nmrfd.hdr);
677
678 switch (lRet)
679 {
680 case RF_CANCEL:
681 EndDialog(hwnd, IDCANCEL);
682 break;
683
684 case RF_OK:
685 /* We use SECL_NO_UI because we don't want to see
686 * errors here, but we will try again below and
687 * there we will output our errors. */
688 if (SUCCEEDED(ShellExecCmdLine(hwnd, pszExpanded, pszStartDir, SW_SHOWNORMAL, NULL,
689 SECL_ALLOW_NONEXE | SECL_NO_UI)))
690 {
691 /* Call GetWindowText again in case the contents of the edit box have changed. */
692 GetWindowTextW(htxt, psz, ic + 1);
693 FillList(htxt, psz, ic + 2 + 1, FALSE);
694 EndDialog(hwnd, IDOK);
695 break;
696 }
697 else if (SUCCEEDED(ShellExecuteExW(&sei)))
698 {
699 /* Call GetWindowText again in case the contents of the edit box have changed. */
700 GetWindowTextW(htxt, psz, ic + 1);
701 FillList(htxt, psz, ic + 2 + 1, FALSE);
702 EndDialog(hwnd, IDOK);
703 break;
704 }
705
706 /* Fall-back */
707 case RF_RETRY:
708 default:
709 SendMessageW(htxt, CB_SETEDITSEL, 0, MAKELPARAM (0, -1));
710 /* Show back the dialog */
711 ShowWindow(hwnd, SW_SHOW);
712 break;
713 }
714
715 HeapFree(GetProcessHeap(), 0, parent);
716 HeapFree(GetProcessHeap(), 0, psz);
717 if (psz != pszExpanded)
718 HeapFree(GetProcessHeap(), 0, pszExpanded);
719 return TRUE;
720 }
721
722 case IDCANCEL:
723 EndDialog(hwnd, IDCANCEL);
724 return TRUE;
725
726 case IDC_RUNDLG_BROWSE:
727 {
728 HMODULE hComdlg = NULL;
729 LPFNOFN ofnProc = NULL;
730 WCHAR szFName[1024] = {0};
731 WCHAR filter[MAX_PATH], szCaption[MAX_PATH];
732 OPENFILENAMEW ofn;
733
734 LoadStringW(shell32_hInstance, IDS_RUNDLG_BROWSE_FILTER, filter, _countof(filter));
735 LoadStringW(shell32_hInstance, IDS_RUNDLG_BROWSE_CAPTION, szCaption, _countof(szCaption));
736
737 ZeroMemory(&ofn, sizeof(ofn));
738 ofn.lStructSize = sizeof(ofn);
739 ofn.hwndOwner = hwnd;
740 ofn.lpstrFilter = filter;
741 ofn.lpstrFile = szFName;
742 ofn.nMaxFile = _countof(szFName) - 1;
743 ofn.lpstrTitle = szCaption;
744 ofn.Flags = OFN_ENABLESIZING | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_EXPLORER;
745 ofn.lpstrInitialDir = prfdp->lpstrDirectory;
746
747 if (NULL == (hComdlg = LoadLibraryExW(L"comdlg32", NULL, 0)) ||
748 NULL == (ofnProc = (LPFNOFN)GetProcAddress(hComdlg, "GetOpenFileNameW")))
749 {
750 ERR("Couldn't get GetOpenFileName function entry (lib=%p, proc=%p)\n", hComdlg, ofnProc);
751 ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_RUNDLG_BROWSE_ERROR), NULL, MB_OK | MB_ICONERROR);
752 return TRUE;
753 }
754
755 if (ofnProc(&ofn))
756 {
757 SetFocus(GetDlgItem(hwnd, IDOK));
758 SetWindowTextW(GetDlgItem(hwnd, IDC_RUNDLG_EDITPATH), szFName);
759 SendMessageW(GetDlgItem(hwnd, IDC_RUNDLG_EDITPATH), CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
760 EnableOkButtonFromEditContents(hwnd);
761 SetFocus(GetDlgItem(hwnd, IDOK));
762 }
763
764 FreeLibrary(hComdlg);
765
766 return TRUE;
767 }
768 case IDC_RUNDLG_EDITPATH:
769 {
770 if (HIWORD(wParam) == CBN_EDITCHANGE)
771 {
772 EnableOkButtonFromEditContents(hwnd);
773 }
774 return TRUE;
775 }
776 }
777 return TRUE;
778 }
779 return FALSE;
780 }
781
782 /*
783 * This function grabs the MRU list from the registry and fills the combo-list
784 * for the "Run" dialog above. fShowDefault is ignored if pszLatest != NULL.
785 */
786 // FIXME: Part of this code should be part of some MRUList API,
787 // that is scattered amongst shell32, comctl32 (?!) and comdlg32.
FillList(HWND hCb,LPWSTR pszLatest,UINT cchStr,BOOL fShowDefault)788 static void FillList(HWND hCb, LPWSTR pszLatest, UINT cchStr, BOOL fShowDefault)
789 {
790 HKEY hkey;
791 WCHAR *pszList = NULL, *pszCmd = NULL, *pszTmp = NULL, cMatch = 0, cMax = 0x60;
792 WCHAR szIndex[2] = L"-";
793 UINT cchLatest;
794 DWORD dwType, icList = 0, icCmd = 0;
795 LRESULT lRet;
796 UINT Nix;
797
798 /*
799 * Retrieve the string length of pszLatest and check whether its buffer size
800 * (cchStr in number of characters) is large enough to add the terminating "\\1"
801 * (and the NULL character).
802 */
803 if (pszLatest)
804 {
805 cchLatest = wcslen(pszLatest);
806 if (cchStr < cchLatest + 2 + 1)
807 {
808 TRACE("pszLatest buffer is not large enough (%d) to hold the MRU terminator.\n", cchStr);
809 return;
810 }
811 }
812 else
813 {
814 cchStr = 0;
815 }
816
817 SendMessageW(hCb, CB_RESETCONTENT, 0, 0);
818
819 lRet = RegCreateKeyExW(HKEY_CURRENT_USER,
820 L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU",
821 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL);
822 if (lRet != ERROR_SUCCESS)
823 {
824 TRACE("Unable to open or create the RunMRU key, error %d\n", GetLastError());
825 return;
826 }
827
828 lRet = RegQueryValueExW(hkey, L"MRUList", NULL, &dwType, NULL, &icList);
829 if (lRet == ERROR_SUCCESS && dwType == REG_SZ && icList > sizeof(WCHAR))
830 {
831 pszList = (WCHAR*)HeapAlloc(GetProcessHeap(), 0, icList);
832 if (!pszList)
833 {
834 TRACE("HeapAlloc failed to allocate %d bytes\n", icList);
835 goto Continue;
836 }
837 pszList[0] = L'\0';
838
839 lRet = RegQueryValueExW(hkey, L"MRUList", NULL, NULL, (LPBYTE)pszList, &icList);
840 if (lRet != ERROR_SUCCESS)
841 {
842 TRACE("Unable to grab MRUList, error %d\n", GetLastError());
843 pszList[0] = L'\0';
844 }
845 }
846 else
847 {
848 Continue:
849 icList = sizeof(WCHAR);
850 pszList = (WCHAR*)HeapAlloc(GetProcessHeap(), 0, icList);
851 if (!pszList)
852 {
853 TRACE("HeapAlloc failed to allocate %d bytes\n", icList);
854 RegCloseKey(hkey);
855 return;
856 }
857 pszList[0] = L'\0';
858 }
859
860 /* Convert the number of bytes from MRUList into number of characters (== number of indices) */
861 icList /= sizeof(WCHAR);
862
863 for (Nix = 0; Nix < icList - 1; Nix++)
864 {
865 if (pszList[Nix] > cMax)
866 cMax = pszList[Nix];
867
868 szIndex[0] = pszList[Nix];
869
870 lRet = RegQueryValueExW(hkey, szIndex, NULL, &dwType, NULL, &icCmd);
871 if (lRet != ERROR_SUCCESS || dwType != REG_SZ)
872 {
873 TRACE("Unable to grab size of index, error %d\n", GetLastError());
874 continue;
875 }
876
877 if (pszCmd)
878 {
879 pszTmp = (WCHAR*)HeapReAlloc(GetProcessHeap(), 0, pszCmd, icCmd);
880 if (!pszTmp)
881 {
882 TRACE("HeapReAlloc failed to reallocate %d bytes\n", icCmd);
883 continue;
884 }
885 pszCmd = pszTmp;
886 }
887 else
888 {
889 pszCmd = (WCHAR*)HeapAlloc(GetProcessHeap(), 0, icCmd);
890 if (!pszCmd)
891 {
892 TRACE("HeapAlloc failed to allocate %d bytes\n", icCmd);
893 continue;
894 }
895 }
896
897 lRet = RegQueryValueExW(hkey, szIndex, NULL, NULL, (LPBYTE)pszCmd, &icCmd);
898 if (lRet != ERROR_SUCCESS)
899 {
900 TRACE("Unable to grab index, error %d\n", GetLastError());
901 continue;
902 }
903
904 /*
905 * Generally the command string will end up with "\\1".
906 * Find the last backslash in the string and NULL-terminate.
907 * Windows does not seem to check for what comes next, so that
908 * a command of the form:
909 * c:\\my_dir\\myfile.exe
910 * will be cut just after "my_dir", whereas a command of the form:
911 * c:\\my_dir\\myfile.exe\\1
912 * will be cut just after "myfile.exe".
913 */
914 pszTmp = wcsrchr(pszCmd, L'\\');
915 if (pszTmp)
916 *pszTmp = L'\0';
917
918 /*
919 * In the following we try to add pszLatest to the MRU list.
920 * We suppose that our caller has already correctly allocated
921 * the string with enough space for us to append a "\\1".
922 *
923 * FIXME: TODO! (At the moment we don't append it!)
924 */
925
926 if (pszLatest)
927 {
928 if (_wcsicmp(pszCmd, pszLatest) == 0)
929 {
930 SendMessageW(hCb, CB_INSERTSTRING, 0, (LPARAM)pszCmd);
931 SetWindowTextW(hCb, pszCmd);
932 SendMessageW(hCb, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
933
934 cMatch = pszList[Nix];
935 memmove(&pszList[1], pszList, Nix * sizeof(WCHAR));
936 pszList[0] = cMatch;
937 continue;
938 }
939 }
940
941 if (icList - 1 != 26 || icList - 2 != Nix || cMatch || pszLatest == NULL)
942 {
943 SendMessageW(hCb, CB_ADDSTRING, 0, (LPARAM)pszCmd);
944 if (!Nix && fShowDefault)
945 {
946 SetWindowTextW(hCb, pszCmd);
947 SendMessageW(hCb, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
948 }
949 }
950 else
951 {
952 SendMessageW(hCb, CB_INSERTSTRING, 0, (LPARAM)pszLatest);
953 SetWindowTextW(hCb, pszLatest);
954 SendMessageW(hCb, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
955
956 cMatch = pszList[Nix];
957 memmove(&pszList[1], pszList, Nix * sizeof(WCHAR));
958 pszList[0] = cMatch;
959 szIndex[0] = cMatch;
960
961 wcscpy(&pszLatest[cchLatest], L"\\1");
962 RegSetValueExW(hkey, szIndex, 0, REG_SZ, (LPBYTE)pszLatest, (cchLatest + 2 + 1) * sizeof(WCHAR));
963 pszLatest[cchLatest] = L'\0';
964 }
965 }
966
967 if (!cMatch && pszLatest != NULL)
968 {
969 SendMessageW(hCb, CB_INSERTSTRING, 0, (LPARAM)pszLatest);
970 SetWindowTextW(hCb, pszLatest);
971 SendMessageW(hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1));
972
973 cMatch = ++cMax;
974
975 if (pszList)
976 {
977 pszTmp = (WCHAR*)HeapReAlloc(GetProcessHeap(), 0, pszList, (++icList) * sizeof(WCHAR));
978 if (!pszTmp)
979 {
980 TRACE("HeapReAlloc failed to reallocate enough bytes\n");
981 goto Cleanup;
982 }
983 pszList = pszTmp;
984 }
985 else
986 {
987 pszList = (WCHAR*)HeapAlloc(GetProcessHeap(), 0, (++icList) * sizeof(WCHAR));
988 if (!pszList)
989 {
990 TRACE("HeapAlloc failed to allocate enough bytes\n");
991 goto Cleanup;
992 }
993 }
994
995 memmove(&pszList[1], pszList, (icList - 1) * sizeof(WCHAR));
996 pszList[0] = cMatch;
997 szIndex[0] = cMatch;
998
999 wcscpy(&pszLatest[cchLatest], L"\\1");
1000 RegSetValueExW(hkey, szIndex, 0, REG_SZ, (LPBYTE)pszLatest, (cchLatest + 2 + 1) * sizeof(WCHAR));
1001 pszLatest[cchLatest] = L'\0';
1002 }
1003
1004 Cleanup:
1005 RegSetValueExW(hkey, L"MRUList", 0, REG_SZ, (LPBYTE)pszList, (wcslen(pszList) + 1) * sizeof(WCHAR));
1006
1007 HeapFree(GetProcessHeap(), 0, pszCmd);
1008 HeapFree(GetProcessHeap(), 0, pszList);
1009
1010 RegCloseKey(hkey);
1011 }
1012
1013
1014 /*************************************************************************
1015 * ConfirmDialog [internal]
1016 *
1017 * Put up a confirm box, return TRUE if the user confirmed
1018 */
ConfirmDialog(HWND hWndOwner,UINT PromptId,UINT TitleId)1019 static BOOL ConfirmDialog(HWND hWndOwner, UINT PromptId, UINT TitleId)
1020 {
1021 WCHAR Prompt[256];
1022 WCHAR Title[256];
1023
1024 LoadStringW(shell32_hInstance, PromptId, Prompt, _countof(Prompt));
1025 LoadStringW(shell32_hInstance, TitleId, Title, _countof(Title));
1026 return MessageBoxW(hWndOwner, Prompt, Title, MB_YESNO | MB_ICONQUESTION) == IDYES;
1027 }
1028
1029 typedef HRESULT (WINAPI *tShellDimScreen)(IUnknown** Unknown, HWND* hWindow);
1030
1031 BOOL
CallShellDimScreen(IUnknown ** pUnknown,HWND * hWindow)1032 CallShellDimScreen(IUnknown** pUnknown, HWND* hWindow)
1033 {
1034 static tShellDimScreen ShellDimScreen;
1035 static BOOL Initialized = FALSE;
1036 if (!Initialized)
1037 {
1038 HMODULE mod = LoadLibraryW(L"msgina.dll");
1039 ShellDimScreen = (tShellDimScreen)GetProcAddress(mod, (LPCSTR)16);
1040 Initialized = TRUE;
1041 }
1042
1043 HRESULT hr = E_FAIL;
1044 if (ShellDimScreen)
1045 hr = ShellDimScreen(pUnknown, hWindow);
1046 return SUCCEEDED(hr);
1047 }
1048
1049
1050 /* Used to get the shutdown privilege */
1051 static BOOL
EnablePrivilege(LPCWSTR lpszPrivilegeName,BOOL bEnablePrivilege)1052 EnablePrivilege(LPCWSTR lpszPrivilegeName, BOOL bEnablePrivilege)
1053 {
1054 BOOL Success;
1055 HANDLE hToken;
1056 TOKEN_PRIVILEGES tp;
1057
1058 Success = OpenProcessToken(GetCurrentProcess(),
1059 TOKEN_ADJUST_PRIVILEGES,
1060 &hToken);
1061 if (!Success) return Success;
1062
1063 Success = LookupPrivilegeValueW(NULL,
1064 lpszPrivilegeName,
1065 &tp.Privileges[0].Luid);
1066 if (!Success) goto Quit;
1067
1068 tp.PrivilegeCount = 1;
1069 tp.Privileges[0].Attributes = (bEnablePrivilege ? SE_PRIVILEGE_ENABLED : 0);
1070
1071 Success = AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
1072
1073 Quit:
1074 CloseHandle(hToken);
1075 return Success;
1076 }
1077
1078 /*************************************************************************
1079 * RestartDialogEx [SHELL32.730]
1080 */
1081
RestartDialogEx(HWND hWndOwner,LPCWSTR lpwstrReason,DWORD uFlags,DWORD uReason)1082 int WINAPI RestartDialogEx(HWND hWndOwner, LPCWSTR lpwstrReason, DWORD uFlags, DWORD uReason)
1083 {
1084 TRACE("(%p)\n", hWndOwner);
1085
1086 CComPtr<IUnknown> fadeHandler;
1087 HWND parent;
1088
1089 if (!CallShellDimScreen(&fadeHandler, &parent))
1090 parent = hWndOwner;
1091
1092 /* FIXME: use lpwstrReason */
1093 if (ConfirmDialog(parent, IDS_RESTART_PROMPT, IDS_RESTART_TITLE))
1094 {
1095 EnablePrivilege(L"SeShutdownPrivilege", TRUE);
1096 ExitWindowsEx(EWX_REBOOT, uReason);
1097 EnablePrivilege(L"SeShutdownPrivilege", FALSE);
1098 }
1099
1100 return 0;
1101 }
1102
1103 /* Functions and macros used for fancy log off dialog box */
1104 #define IS_PRODUCT_VERSION_WORKSTATION 0x300
1105 #define FRIENDLY_LOGOFF_IS_NOT_ENFORCED 0x0
1106
1107 #define FONT_POINT_SIZE 13
1108
1109 #define DARK_GREY_COLOR RGB(244, 244, 244)
1110 #define LIGHT_GREY_COLOR RGB(38, 38, 38)
1111
1112 /* Bitmap's size for buttons */
1113 #define CX_BITMAP 33
1114 #define CY_BITMAP 33
1115
1116 #define NUMBER_OF_BUTTONS 2
1117
1118 /* After determining the button as well as its state paint the image strip bitmap using these predefined positions */
1119 #define BUTTON_SWITCH_USER 0
1120 #define BUTTON_SWITCH_USER_PRESSED (CY_BITMAP + BUTTON_SWITCH_USER)
1121 #define BUTTON_SWITCH_USER_FOCUSED (CY_BITMAP + BUTTON_SWITCH_USER_PRESSED)
1122 #define BUTTON_LOG_OFF (CY_BITMAP + BUTTON_SWITCH_USER_FOCUSED)
1123 #define BUTTON_LOG_OFF_PRESSED (CY_BITMAP + BUTTON_LOG_OFF)
1124 #define BUTTON_LOG_OFF_FOCUSED (CY_BITMAP + BUTTON_LOG_OFF_PRESSED)
1125 #define BUTTON_SWITCH_USER_DISABLED (CY_BITMAP + BUTTON_LOG_OFF_FOCUSED) // Temporary
1126
1127 /* For bIsButtonHot */
1128 #define LOG_OFF_BUTTON_HOT 0
1129 #define SWITCH_USER_BUTTON_HOT 1
1130
DrawIconOnOwnerDrawnButtons(DRAWITEMSTRUCT * pdis,PLOGOFF_DLG_CONTEXT pContext)1131 BOOL DrawIconOnOwnerDrawnButtons(DRAWITEMSTRUCT* pdis, PLOGOFF_DLG_CONTEXT pContext)
1132 {
1133 BOOL bRet = FALSE;
1134 HDC hdcMem = NULL;
1135 HBITMAP hbmOld = NULL;
1136 int y = 0;
1137 RECT rect;
1138
1139 hdcMem = CreateCompatibleDC(pdis->hDC);
1140 hbmOld = (HBITMAP)SelectObject(hdcMem, pContext->hImageStrip);
1141 rect = pdis->rcItem;
1142
1143 /* Check the button ID for revelant bitmap to be used */
1144 switch (pdis->CtlID)
1145 {
1146 case IDC_LOG_OFF_BUTTON:
1147 {
1148 switch (pdis->itemAction)
1149 {
1150 case ODA_DRAWENTIRE:
1151 case ODA_FOCUS:
1152 case ODA_SELECT:
1153 {
1154 y = BUTTON_LOG_OFF;
1155 if (pdis->itemState & ODS_SELECTED)
1156 {
1157 y = BUTTON_LOG_OFF_PRESSED;
1158 }
1159 else if (pContext->bIsButtonHot[LOG_OFF_BUTTON_HOT] || (pdis->itemState & ODS_FOCUS))
1160 {
1161 y = BUTTON_LOG_OFF_FOCUSED;
1162 }
1163 break;
1164 }
1165 }
1166 break;
1167 }
1168
1169 case IDC_SWITCH_USER_BUTTON:
1170 {
1171 switch (pdis->itemAction)
1172 {
1173 case ODA_DRAWENTIRE:
1174 case ODA_FOCUS:
1175 case ODA_SELECT:
1176 {
1177 y = BUTTON_SWITCH_USER;
1178 if (pdis->itemState & ODS_SELECTED)
1179 {
1180 y = BUTTON_SWITCH_USER_PRESSED;
1181 }
1182 else if (pContext->bIsButtonHot[SWITCH_USER_BUTTON_HOT] || (pdis->itemState & ODS_FOCUS))
1183 {
1184 y = BUTTON_SWITCH_USER_FOCUSED;
1185 }
1186
1187 /*
1188 * Since switch user functionality isn't implemented yet therefore the button has been disabled
1189 * temporarily hence show the disabled state
1190 */
1191 else if (pdis->itemState & ODS_DISABLED)
1192 {
1193 y = BUTTON_SWITCH_USER_DISABLED;
1194 }
1195 break;
1196 }
1197 }
1198 break;
1199 }
1200 }
1201
1202 /* Draw it on the required button */
1203 bRet = BitBlt(pdis->hDC,
1204 (rect.right - rect.left - CX_BITMAP) / 2,
1205 (rect.bottom - rect.top - CY_BITMAP) / 2,
1206 CX_BITMAP, CY_BITMAP, hdcMem, 0, y, SRCCOPY);
1207
1208 SelectObject(hdcMem, hbmOld);
1209 DeleteDC(hdcMem);
1210
1211 return bRet;
1212 }
1213
OwnerDrawButtonSubclass(HWND hButton,UINT uMsg,WPARAM wParam,LPARAM lParam)1214 INT_PTR CALLBACK OwnerDrawButtonSubclass(HWND hButton, UINT uMsg, WPARAM wParam, LPARAM lParam)
1215 {
1216 PLOGOFF_DLG_CONTEXT pContext;
1217 pContext = (PLOGOFF_DLG_CONTEXT)GetWindowLongPtrW(GetParent(hButton), GWLP_USERDATA);
1218
1219 int buttonID = GetDlgCtrlID(hButton);
1220
1221 switch (uMsg)
1222 {
1223 case WM_MOUSEMOVE:
1224 {
1225 HWND hwndTarget = NULL;
1226 POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};;
1227
1228 if (GetCapture() != hButton)
1229 {
1230 SetCapture(hButton);
1231
1232 switch (buttonID)
1233 {
1234 case IDC_LOG_OFF_BUTTON:
1235 {
1236 pContext->bIsButtonHot[LOG_OFF_BUTTON_HOT] = TRUE;
1237 break;
1238 }
1239 case IDC_SWITCH_USER_BUTTON:
1240 {
1241 pContext->bIsButtonHot[SWITCH_USER_BUTTON_HOT] = TRUE;
1242 break;
1243 }
1244 }
1245 SetCursor(LoadCursorW(NULL, IDC_HAND));
1246 }
1247
1248 ClientToScreen(hButton, &pt);
1249 hwndTarget = WindowFromPoint(pt);
1250
1251 if (hwndTarget != hButton)
1252 {
1253 ReleaseCapture();
1254
1255 switch (buttonID)
1256 {
1257 case IDC_LOG_OFF_BUTTON:
1258 {
1259 pContext->bIsButtonHot[LOG_OFF_BUTTON_HOT] = FALSE;
1260 break;
1261 }
1262 case IDC_SWITCH_USER_BUTTON:
1263 {
1264 pContext->bIsButtonHot[SWITCH_USER_BUTTON_HOT] = FALSE;
1265 break;
1266 }
1267 }
1268 }
1269 InvalidateRect(hButton, NULL, FALSE);
1270 break;
1271 }
1272
1273 /* Whenever one of the buttons gets the keyboard focus, set it as default button */
1274 case WM_SETFOCUS:
1275 {
1276 SendMessageW(GetParent(hButton), DM_SETDEFID, buttonID, 0);
1277 break;
1278 }
1279
1280 /* Otherwise, set IDCANCEL as default button */
1281 case WM_KILLFOCUS:
1282 {
1283 SendMessageW(GetParent(hButton), DM_SETDEFID, IDCANCEL, 0);
1284 break;
1285 }
1286 }
1287 return CallWindowProcW(pContext->OldButtonProc, hButton, uMsg, wParam, lParam);
1288 }
1289
CreateToolTipForButtons(int controlID,int detailID,HWND hDlg,int titleID)1290 VOID CreateToolTipForButtons(int controlID, int detailID, HWND hDlg, int titleID)
1291 {
1292 HWND hwndTool = NULL, hwndTip = NULL;
1293 WCHAR szBuffer[256];
1294 TTTOOLINFOW tool;
1295
1296 hwndTool = GetDlgItem(hDlg, controlID);
1297
1298 tool.cbSize = sizeof(tool);
1299 tool.hwnd = hDlg;
1300 tool.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
1301 tool.uId = (UINT_PTR)hwndTool;
1302
1303 /* Create the tooltip */
1304 hwndTip = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL,
1305 WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,
1306 CW_USEDEFAULT, CW_USEDEFAULT,
1307 CW_USEDEFAULT, CW_USEDEFAULT,
1308 hDlg, NULL, shell32_hInstance, NULL);
1309
1310 /* Associate the tooltip with the tool. */
1311 LoadStringW(shell32_hInstance, detailID, szBuffer, _countof(szBuffer));
1312 tool.lpszText = szBuffer;
1313 SendMessageW(hwndTip, TTM_ADDTOOLW, 0, (LPARAM)&tool);
1314 LoadStringW(shell32_hInstance, titleID, szBuffer, _countof(szBuffer));
1315 SendMessageW(hwndTip, TTM_SETTITLEW, TTI_NONE, (LPARAM)szBuffer);
1316 SendMessageW(hwndTip, TTM_SETMAXTIPWIDTH, 0, 250);
1317 }
1318
EndFriendlyDialog(HWND hwnd,PLOGOFF_DLG_CONTEXT pContext)1319 VOID EndFriendlyDialog(HWND hwnd, PLOGOFF_DLG_CONTEXT pContext)
1320 {
1321 DeleteObject(pContext->hBrush);
1322 DeleteObject(pContext->hImageStrip);
1323 DeleteObject(pContext->hfFont);
1324
1325 /* Remove the subclass from the buttons */
1326 for (int i = 0; i < NUMBER_OF_BUTTONS; i++)
1327 {
1328 SetWindowLongPtrW(GetDlgItem(hwnd, IDC_LOG_OFF_BUTTON + i),
1329 GWLP_WNDPROC,
1330 (LONG_PTR)pContext->OldButtonProc);
1331 }
1332 }
1333
IsFriendlyUIActive(VOID)1334 static BOOL IsFriendlyUIActive(VOID)
1335 {
1336 DWORD dwType = 0, dwValue = 0, dwSize = 0;
1337 HKEY hKey = NULL;
1338 LONG lRet = 0;
1339
1340 lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1341 L"SYSTEM\\CurrentControlSet\\Control\\Windows",
1342 0,
1343 KEY_QUERY_VALUE,
1344 &hKey);
1345 if (lRet != ERROR_SUCCESS)
1346 return FALSE;
1347
1348 /* First check an optional ReactOS specific override, that Windows does not check.
1349 We use this to allow users pairing 'Server'-configuration with FriendlyLogoff.
1350 Otherwise users would have to change CSDVersion or LogonType (side-effects AppCompat) */
1351 dwValue = 0;
1352 dwSize = sizeof(dwValue);
1353 lRet = RegQueryValueExW(hKey,
1354 L"EnforceFriendlyLogoff",
1355 NULL,
1356 &dwType,
1357 (LPBYTE)&dwValue,
1358 &dwSize);
1359
1360 if (lRet == ERROR_SUCCESS && dwType == REG_DWORD && dwValue != FRIENDLY_LOGOFF_IS_NOT_ENFORCED)
1361 {
1362 RegCloseKey(hKey);
1363 return TRUE;
1364 }
1365
1366 /* Check product version number */
1367 dwValue = 0;
1368 dwSize = sizeof(dwValue);
1369 lRet = RegQueryValueExW(hKey,
1370 L"CSDVersion",
1371 NULL,
1372 &dwType,
1373 (LPBYTE)&dwValue,
1374 &dwSize);
1375 RegCloseKey(hKey);
1376
1377 if (lRet != ERROR_SUCCESS || dwType != REG_DWORD || dwValue != IS_PRODUCT_VERSION_WORKSTATION)
1378 {
1379 /* Allow Friendly UI only on Workstation */
1380 return FALSE;
1381 }
1382
1383 /* Check LogonType value */
1384 lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1385 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
1386 0,
1387 KEY_QUERY_VALUE,
1388 &hKey);
1389 if (lRet != ERROR_SUCCESS)
1390 return FALSE;
1391
1392 dwValue = 0;
1393 dwSize = sizeof(dwValue);
1394 lRet = RegQueryValueExW(hKey,
1395 L"LogonType",
1396 NULL,
1397 &dwType,
1398 (LPBYTE)&dwValue,
1399 &dwSize);
1400 RegCloseKey(hKey);
1401
1402 if (lRet != ERROR_SUCCESS || dwType != REG_DWORD)
1403 return FALSE;
1404
1405 return (dwValue != 0);
1406 }
1407
FancyLogoffOnInit(HWND hwnd,PLOGOFF_DLG_CONTEXT pContext)1408 static VOID FancyLogoffOnInit(HWND hwnd, PLOGOFF_DLG_CONTEXT pContext)
1409 {
1410 HDC hdc = NULL;
1411 LONG lfHeight = NULL;
1412
1413 hdc = GetDC(NULL);
1414 lfHeight = -MulDiv(FONT_POINT_SIZE, GetDeviceCaps(hdc, LOGPIXELSY), 72);
1415 ReleaseDC(NULL, hdc);
1416 pContext->hfFont = CreateFontW(lfHeight, 0, 0, 0, FW_MEDIUM, FALSE, 0, 0, 0, 0, 0, 0, 0, L"MS Shell Dlg");
1417 SendDlgItemMessageW(hwnd, IDC_LOG_OFF_TEXT_STATIC, WM_SETFONT, (WPARAM)pContext->hfFont, TRUE);
1418
1419 pContext->hBrush = CreateSolidBrush(DARK_GREY_COLOR);
1420
1421 pContext->hImageStrip = LoadBitmapW(shell32_hInstance, MAKEINTRESOURCEW(IDB_IMAGE_STRIP));
1422
1423 /* Gather old button func */
1424 pContext->OldButtonProc = (WNDPROC)GetWindowLongPtrW(GetDlgItem(hwnd, IDC_LOG_OFF_BUTTON), GWLP_WNDPROC);
1425
1426 /* Set bIsButtonHot to false, create tooltips for each buttons and subclass the buttons */
1427 for (int i = 0; i < NUMBER_OF_BUTTONS; i++)
1428 {
1429 pContext->bIsButtonHot[i] = FALSE;
1430 SetWindowLongPtrW(GetDlgItem(hwnd, IDC_LOG_OFF_BUTTON + i),
1431 GWLP_WNDPROC,
1432 (LONG_PTR)OwnerDrawButtonSubclass);
1433 CreateToolTipForButtons(IDC_LOG_OFF_BUTTON + i,
1434 IDS_LOG_OFF_DESC + i,
1435 hwnd,
1436 IDS_LOG_OFF_TITLE + i);
1437 }
1438 }
1439
1440 /*************************************************************************
1441 * LogOffDialogProc
1442 *
1443 * NOTES: Used to make the Log Off dialog work
1444 */
LogOffDialogProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)1445 INT_PTR CALLBACK LogOffDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1446 {
1447 DRAWITEMSTRUCT* pdis = (DRAWITEMSTRUCT*)lParam;
1448 PLOGOFF_DLG_CONTEXT pContext;
1449 pContext = (PLOGOFF_DLG_CONTEXT)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1450
1451 switch (uMsg)
1452 {
1453 case WM_INITDIALOG:
1454 {
1455 pContext = (PLOGOFF_DLG_CONTEXT)lParam;
1456 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)pContext);
1457
1458 if (pContext->bFriendlyUI)
1459 FancyLogoffOnInit(hwnd, pContext);
1460 return TRUE;
1461 }
1462
1463 case WM_CLOSE:
1464 EndDialog(hwnd, IDCANCEL);
1465 break;
1466
1467 /*
1468 * If the user deactivates the log off dialog (it loses its focus
1469 * while the dialog is not being closed), then destroy the dialog
1470 * box.
1471 */
1472 case WM_ACTIVATE:
1473 {
1474 if (LOWORD(wParam) == WA_INACTIVE)
1475 {
1476 EndDialog(hwnd, IDCANCEL);
1477 }
1478 return FALSE;
1479 }
1480
1481 case WM_COMMAND:
1482 switch (LOWORD(wParam))
1483 {
1484 case IDC_LOG_OFF_BUTTON:
1485 case IDOK:
1486 ExitWindowsEx(EWX_LOGOFF, 0);
1487 break;
1488
1489 case IDCANCEL:
1490 EndDialog(hwnd, IDCANCEL);
1491 break;
1492 }
1493 break;
1494
1495 case WM_DESTROY:
1496 if (pContext->bFriendlyUI)
1497 EndFriendlyDialog(hwnd, pContext);
1498 return TRUE;
1499
1500 case WM_CTLCOLORSTATIC:
1501 {
1502 /* Either make background transparent or fill it with color for required static controls */
1503 HDC hdcStatic = (HDC)wParam;
1504 UINT StaticID = (UINT)GetWindowLongPtrW((HWND)lParam, GWL_ID);
1505
1506 switch (StaticID)
1507 {
1508 case IDC_LOG_OFF_TEXT_STATIC:
1509 SetTextColor(hdcStatic, DARK_GREY_COLOR);
1510 SetBkMode(hdcStatic, TRANSPARENT);
1511 return (INT_PTR)GetStockObject(HOLLOW_BRUSH);
1512
1513 case IDC_LOG_OFF_STATIC:
1514 case IDC_SWITCH_USER_STATIC:
1515 SetTextColor(hdcStatic, LIGHT_GREY_COLOR);
1516 SetBkMode(hdcStatic, TRANSPARENT);
1517 return (LONG_PTR)pContext->hBrush;
1518 }
1519 return FALSE;
1520 }
1521 break;
1522
1523 case WM_DRAWITEM:
1524 {
1525 /* Draw bitmaps on required buttons */
1526 switch (pdis->CtlID)
1527 {
1528 case IDC_LOG_OFF_BUTTON:
1529 case IDC_SWITCH_USER_BUTTON:
1530 return DrawIconOnOwnerDrawnButtons(pdis, pContext);
1531 }
1532 }
1533 break;
1534
1535 default:
1536 break;
1537 }
1538 return FALSE;
1539 }
1540
1541 /*************************************************************************
1542 * LogoffWindowsDialog [SHELL32.54]
1543 */
1544
LogoffWindowsDialog(HWND hWndOwner)1545 EXTERN_C int WINAPI LogoffWindowsDialog(HWND hWndOwner)
1546 {
1547 CComPtr<IUnknown> fadeHandler;
1548 HWND parent = NULL;
1549 DWORD LogoffDialogID = IDD_LOG_OFF;
1550 LOGOFF_DLG_CONTEXT Context;
1551
1552 if (!CallShellDimScreen(&fadeHandler, &parent))
1553 parent = hWndOwner;
1554
1555 Context.bFriendlyUI = IsFriendlyUIActive();
1556 if (Context.bFriendlyUI)
1557 {
1558 LogoffDialogID = IDD_LOG_OFF_FANCY;
1559 }
1560
1561 DialogBoxParamW(shell32_hInstance,
1562 MAKEINTRESOURCEW(LogoffDialogID),
1563 parent,
1564 LogOffDialogProc,
1565 (LPARAM)&Context);
1566 return 0;
1567 }
1568
1569 /*************************************************************************
1570 * RestartDialog [SHELL32.59]
1571 */
1572
RestartDialog(HWND hWndOwner,LPCWSTR lpstrReason,DWORD uFlags)1573 int WINAPI RestartDialog(HWND hWndOwner, LPCWSTR lpstrReason, DWORD uFlags)
1574 {
1575 return RestartDialogEx(hWndOwner, lpstrReason, uFlags, 0);
1576 }
1577
1578 /*************************************************************************
1579 * ExitWindowsDialog_backup
1580 *
1581 * NOTES
1582 * Used as a backup solution to shutdown the OS in case msgina.dll
1583 * somehow cannot be found.
1584 */
ExitWindowsDialog_backup(HWND hWndOwner)1585 VOID ExitWindowsDialog_backup(HWND hWndOwner)
1586 {
1587 TRACE("(%p)\n", hWndOwner);
1588
1589 if (ConfirmDialog(hWndOwner, IDS_SHUTDOWN_PROMPT, IDS_SHUTDOWN_TITLE))
1590 {
1591 EnablePrivilege(L"SeShutdownPrivilege", TRUE);
1592 ExitWindowsEx(EWX_SHUTDOWN, 0);
1593 EnablePrivilege(L"SeShutdownPrivilege", FALSE);
1594 }
1595 }
1596
1597 /*************************************************************************
1598 * ExitWindowsDialog [SHELL32.60]
1599 *
1600 * NOTES
1601 * exported by ordinal
1602 */
1603 /*
1604 * TODO:
1605 * - Implement the ability to show either the Welcome Screen or the classic dialog boxes based upon the
1606 * registry value: SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\LogonType.
1607 */
ExitWindowsDialog(HWND hWndOwner)1608 void WINAPI ExitWindowsDialog(HWND hWndOwner)
1609 {
1610 typedef DWORD (WINAPI *ShellShFunc)(HWND hParent, WCHAR *Username, BOOL bHideLogoff);
1611 HINSTANCE msginaDll = LoadLibraryW(L"msgina.dll");
1612
1613 TRACE("(%p)\n", hWndOwner);
1614
1615 CComPtr<IUnknown> fadeHandler;
1616 HWND parent;
1617 if (!CallShellDimScreen(&fadeHandler, &parent))
1618 parent = hWndOwner;
1619
1620 /* If the DLL cannot be found for any reason, then it simply uses a
1621 dialog box to ask if the user wants to shut down the computer. */
1622 if (!msginaDll)
1623 {
1624 TRACE("Unable to load msgina.dll.\n");
1625 ExitWindowsDialog_backup(parent);
1626 return;
1627 }
1628
1629 ShellShFunc pShellShutdownDialog = (ShellShFunc)GetProcAddress(msginaDll, "ShellShutdownDialog");
1630
1631 if (pShellShutdownDialog)
1632 {
1633 /* Actually call the function */
1634 DWORD returnValue = pShellShutdownDialog(parent, NULL, FALSE);
1635
1636 switch (returnValue)
1637 {
1638 case 0x01: /* Log off user */
1639 {
1640 ExitWindowsEx(EWX_LOGOFF, 0);
1641 break;
1642 }
1643 case 0x02: /* Shut down */
1644 {
1645 EnablePrivilege(L"SeShutdownPrivilege", TRUE);
1646 ExitWindowsEx(EWX_SHUTDOWN, 0);
1647 EnablePrivilege(L"SeShutdownPrivilege", FALSE);
1648 break;
1649 }
1650 case 0x03: /* Install Updates/Shutdown (?) */
1651 {
1652 break;
1653 }
1654 case 0x04: /* Reboot */
1655 {
1656 EnablePrivilege(L"SeShutdownPrivilege", TRUE);
1657 ExitWindowsEx(EWX_REBOOT, 0);
1658 EnablePrivilege(L"SeShutdownPrivilege", FALSE);
1659 break;
1660 }
1661 case 0x10: /* Sleep */
1662 {
1663 if (IsPwrSuspendAllowed())
1664 {
1665 EnablePrivilege(L"SeShutdownPrivilege", TRUE);
1666 SetSuspendState(FALSE, FALSE, FALSE);
1667 EnablePrivilege(L"SeShutdownPrivilege", FALSE);
1668 }
1669 break;
1670 }
1671 case 0x40: /* Hibernate */
1672 {
1673 if (IsPwrHibernateAllowed())
1674 {
1675 EnablePrivilege(L"SeShutdownPrivilege", TRUE);
1676 SetSuspendState(TRUE, FALSE, TRUE);
1677 EnablePrivilege(L"SeShutdownPrivilege", FALSE);
1678 }
1679 break;
1680 }
1681 /* If the option is any other value */
1682 default:
1683 break;
1684 }
1685 }
1686 else
1687 {
1688 /* If the function cannot be found, then revert to using the backup solution */
1689 TRACE("Unable to find the 'ShellShutdownDialog' function");
1690 ExitWindowsDialog_backup(parent);
1691 }
1692
1693 FreeLibrary(msginaDll);
1694 }
1695