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 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 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 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 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 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 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 */ 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 */ 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 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 */ 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. 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 */ 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 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 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 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 BOOL DrawIconOnOwnerDrawnButtons(DRAWITEMSTRUCT* pdis, PLOGOFF_DLG_CONTEXT pContext) 1128 { 1129 BOOL bRet = FALSE; 1130 HDC hdcMem = NULL; 1131 HBITMAP hbmOld = NULL; 1132 int y = 0; 1133 RECT rect; 1134 1135 hdcMem = CreateCompatibleDC(pdis->hDC); 1136 hbmOld = (HBITMAP)SelectObject(hdcMem, pContext->hImageStrip); 1137 rect = pdis->rcItem; 1138 1139 /* Check the button ID for revelant bitmap to be used */ 1140 switch (pdis->CtlID) 1141 { 1142 case IDC_LOG_OFF_BUTTON: 1143 { 1144 switch (pdis->itemAction) 1145 { 1146 case ODA_DRAWENTIRE: 1147 case ODA_FOCUS: 1148 case ODA_SELECT: 1149 { 1150 y = BUTTON_LOG_OFF; 1151 if (pdis->itemState & ODS_SELECTED) 1152 { 1153 y = BUTTON_LOG_OFF_PRESSED; 1154 } 1155 else if (pContext->bIsButtonHot[0] || (pdis->itemState & ODS_FOCUS)) 1156 { 1157 y = BUTTON_LOG_OFF_FOCUSED; 1158 } 1159 break; 1160 } 1161 } 1162 break; 1163 } 1164 1165 case IDC_SWITCH_USER_BUTTON: 1166 { 1167 switch (pdis->itemAction) 1168 { 1169 case ODA_DRAWENTIRE: 1170 case ODA_FOCUS: 1171 case ODA_SELECT: 1172 { 1173 y = BUTTON_SWITCH_USER; 1174 if (pdis->itemState & ODS_SELECTED) 1175 { 1176 y = BUTTON_SWITCH_USER_PRESSED; 1177 } 1178 else if (pContext->bIsButtonHot[1] || (pdis->itemState & ODS_FOCUS)) 1179 { 1180 y = BUTTON_SWITCH_USER_FOCUSED; 1181 } 1182 1183 /* 1184 * Since switch user functionality isn't implemented yet therefore the button has been disabled 1185 * temporarily hence show the disabled state 1186 */ 1187 else if (pdis->itemState & ODS_DISABLED) 1188 { 1189 y = BUTTON_SWITCH_USER_DISABLED; 1190 } 1191 break; 1192 } 1193 } 1194 break; 1195 } 1196 } 1197 1198 /* Draw it on the required button */ 1199 bRet = BitBlt(pdis->hDC, 1200 (rect.right - rect.left - CX_BITMAP) / 2, 1201 (rect.bottom - rect.top - CY_BITMAP) / 2, 1202 CX_BITMAP, CY_BITMAP, hdcMem, 0, y, SRCCOPY); 1203 1204 SelectObject(hdcMem, hbmOld); 1205 DeleteDC(hdcMem); 1206 1207 return bRet; 1208 } 1209 1210 INT_PTR CALLBACK OwnerDrawButtonSubclass(HWND hButton, UINT uMsg, WPARAM wParam, LPARAM lParam) 1211 { 1212 PLOGOFF_DLG_CONTEXT pContext; 1213 pContext = (PLOGOFF_DLG_CONTEXT)GetWindowLongPtrW(hButton, GWLP_USERDATA); 1214 1215 int buttonID = GetDlgCtrlID(hButton); 1216 1217 switch (uMsg) 1218 { 1219 case WM_MOUSEMOVE: 1220 { 1221 HWND hwndTarget = NULL; 1222 POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};; 1223 1224 if (GetCapture() != hButton) 1225 { 1226 SetCapture(hButton); 1227 if (buttonID == IDC_LOG_OFF_BUTTON) 1228 { 1229 pContext->bIsButtonHot[0] = TRUE; 1230 } 1231 else if (buttonID == IDC_SWITCH_USER_BUTTON) 1232 { 1233 pContext->bIsButtonHot[1] = TRUE; 1234 } 1235 SetCursor(LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_HAND))); 1236 } 1237 1238 ClientToScreen(hButton, &pt); 1239 hwndTarget = WindowFromPoint(pt); 1240 1241 if (hwndTarget != hButton) 1242 { 1243 ReleaseCapture(); 1244 if (buttonID == IDC_LOG_OFF_BUTTON) 1245 { 1246 pContext->bIsButtonHot[0] = FALSE; 1247 } 1248 else if (buttonID == IDC_SWITCH_USER_BUTTON) 1249 { 1250 pContext->bIsButtonHot[1] = FALSE; 1251 } 1252 } 1253 InvalidateRect(hButton, NULL, FALSE); 1254 break; 1255 } 1256 1257 /* Whenever one of the buttons gets the keyboard focus, set it as default button */ 1258 case WM_SETFOCUS: 1259 { 1260 SendMessageW(GetParent(hButton), DM_SETDEFID, buttonID, 0); 1261 break; 1262 } 1263 1264 /* Otherwise, set IDCANCEL as default button */ 1265 case WM_KILLFOCUS: 1266 { 1267 SendMessageW(GetParent(hButton), DM_SETDEFID, IDCANCEL, 0); 1268 break; 1269 } 1270 } 1271 return CallWindowProcW(pContext->OldButtonProc, hButton, uMsg, wParam, lParam); 1272 } 1273 1274 VOID CreateToolTipForButtons(int controlID, int detailID, HWND hDlg, int titleID) 1275 { 1276 HWND hwndTool = NULL, hwndTip = NULL; 1277 WCHAR szBuffer[256]; 1278 TTTOOLINFOW tool; 1279 1280 hwndTool = GetDlgItem(hDlg, controlID); 1281 1282 tool.cbSize = sizeof(tool); 1283 tool.hwnd = hDlg; 1284 tool.uFlags = TTF_IDISHWND | TTF_SUBCLASS; 1285 tool.uId = (UINT_PTR)hwndTool; 1286 1287 /* Create the tooltip */ 1288 hwndTip = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL, 1289 WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON, 1290 CW_USEDEFAULT, CW_USEDEFAULT, 1291 CW_USEDEFAULT, CW_USEDEFAULT, 1292 hDlg, NULL, shell32_hInstance, NULL); 1293 1294 /* Associate the tooltip with the tool. */ 1295 LoadStringW(shell32_hInstance, detailID, szBuffer, _countof(szBuffer)); 1296 tool.lpszText = szBuffer; 1297 SendMessageW(hwndTip, TTM_ADDTOOLW, 0, (LPARAM)&tool); 1298 LoadStringW(shell32_hInstance, titleID, szBuffer, _countof(szBuffer)); 1299 SendMessageW(hwndTip, TTM_SETTITLEW, TTI_NONE, (LPARAM)szBuffer); 1300 SendMessageW(hwndTip, TTM_SETMAXTIPWIDTH, 0, 250); 1301 } 1302 1303 static BOOL IsFriendlyUIActive(VOID) 1304 { 1305 DWORD dwType = 0, dwValue = 0, dwSize = 0; 1306 HKEY hKey = NULL; 1307 LONG lRet = 0; 1308 1309 lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 1310 L"SYSTEM\\CurrentControlSet\\Control\\Windows", 1311 0, 1312 KEY_QUERY_VALUE, 1313 &hKey); 1314 if (lRet != ERROR_SUCCESS) 1315 return FALSE; 1316 1317 /* First check an optional ReactOS specific override, that Windows does not check. 1318 We use this to allow users pairing 'Server'-configuration with FriendlyLogoff. 1319 Otherwise users would have to change CSDVersion or LogonType (side-effects AppCompat) */ 1320 dwValue = 0; 1321 dwSize = sizeof(dwValue); 1322 lRet = RegQueryValueExW(hKey, 1323 L"EnforceFriendlyLogoff", 1324 NULL, 1325 &dwType, 1326 (LPBYTE)&dwValue, 1327 &dwSize); 1328 1329 if (lRet == ERROR_SUCCESS && dwType == REG_DWORD && dwValue != FRIENDLY_LOGOFF_IS_NOT_ENFORCED) 1330 { 1331 RegCloseKey(hKey); 1332 return TRUE; 1333 } 1334 1335 /* Check product version number */ 1336 dwValue = 0; 1337 dwSize = sizeof(dwValue); 1338 lRet = RegQueryValueExW(hKey, 1339 L"CSDVersion", 1340 NULL, 1341 &dwType, 1342 (LPBYTE)&dwValue, 1343 &dwSize); 1344 RegCloseKey(hKey); 1345 1346 if (lRet != ERROR_SUCCESS || dwType != REG_DWORD || dwValue != IS_PRODUCT_VERSION_WORKSTATION) 1347 { 1348 /* Allow Friendly UI only on Workstation */ 1349 return FALSE; 1350 } 1351 1352 /* Check LogonType value */ 1353 lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 1354 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", 1355 0, 1356 KEY_QUERY_VALUE, 1357 &hKey); 1358 if (lRet != ERROR_SUCCESS) 1359 return FALSE; 1360 1361 dwValue = 0; 1362 dwSize = sizeof(dwValue); 1363 lRet = RegQueryValueExW(hKey, 1364 L"LogonType", 1365 NULL, 1366 &dwType, 1367 (LPBYTE)&dwValue, 1368 &dwSize); 1369 RegCloseKey(hKey); 1370 1371 if (lRet != ERROR_SUCCESS || dwType != REG_DWORD) 1372 return FALSE; 1373 1374 return (dwValue != 0); 1375 } 1376 1377 static VOID FancyLogoffOnInit(HWND hwnd, PLOGOFF_DLG_CONTEXT pContext) 1378 { 1379 HDC hdc = NULL; 1380 LONG lfHeight = NULL; 1381 1382 hdc = GetDC(NULL); 1383 lfHeight = -MulDiv(FONT_POINT_SIZE, GetDeviceCaps(hdc, LOGPIXELSY), 72); 1384 ReleaseDC(NULL, hdc); 1385 pContext->hfFont = CreateFontW(lfHeight, 0, 0, 0, FW_MEDIUM, FALSE, 0, 0, 0, 0, 0, 0, 0, L"MS Shell Dlg"); 1386 SendDlgItemMessageW(hwnd, IDC_LOG_OFF_TEXT_STATIC, WM_SETFONT, (WPARAM)pContext->hfFont, TRUE); 1387 1388 pContext->hBrush = CreateSolidBrush(DARK_GREY_COLOR); 1389 1390 pContext->hImageStrip = LoadBitmapW(shell32_hInstance, MAKEINTRESOURCEW(IDB_IMAGE_STRIP)); 1391 1392 CreateToolTipForButtons(IDC_LOG_OFF_BUTTON, IDS_LOG_OFF_DESC, hwnd, IDS_LOG_OFF_TITLE); 1393 CreateToolTipForButtons(IDC_SWITCH_USER_BUTTON, IDS_SWITCH_USER_DESC, hwnd, IDS_SWITCH_USER_TITLE); 1394 1395 /* Gather old button func */ 1396 pContext->OldButtonProc = (WNDPROC)GetWindowLongPtrW(GetDlgItem(hwnd, IDC_LOG_OFF_BUTTON), GWLP_WNDPROC); 1397 1398 /* Make buttons to remember pContext and subclass the buttons as well as set bIsButtonHot boolean flags to false */ 1399 for (int i = 0; i < NUMBER_OF_BUTTONS; i++) 1400 { 1401 pContext->bIsButtonHot[i] = FALSE; 1402 SetWindowLongPtrW(GetDlgItem(hwnd, IDC_LOG_OFF_BUTTON + i), GWLP_USERDATA, (LONG_PTR)pContext); 1403 SetWindowLongPtrW(GetDlgItem(hwnd, IDC_LOG_OFF_BUTTON + i), GWLP_WNDPROC, (LONG_PTR)OwnerDrawButtonSubclass); 1404 } 1405 } 1406 1407 /************************************************************************* 1408 * LogOffDialogProc 1409 * 1410 * NOTES: Used to make the Log Off dialog work 1411 */ 1412 INT_PTR CALLBACK LogOffDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 1413 { 1414 DRAWITEMSTRUCT* pdis = (DRAWITEMSTRUCT*)lParam; 1415 PLOGOFF_DLG_CONTEXT pContext; 1416 pContext = (PLOGOFF_DLG_CONTEXT)GetWindowLongPtrW(hwnd, GWLP_USERDATA); 1417 1418 switch (uMsg) 1419 { 1420 case WM_INITDIALOG: 1421 { 1422 pContext = (PLOGOFF_DLG_CONTEXT)lParam; 1423 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)pContext); 1424 1425 if (pContext->bFriendlyUI) 1426 FancyLogoffOnInit(hwnd, pContext); 1427 return TRUE; 1428 } 1429 1430 case WM_CLOSE: 1431 DestroyWindow(hwnd); 1432 PostQuitMessage(IDCANCEL); 1433 break; 1434 1435 /* 1436 * If the user deactivates the log off dialog (it loses its focus 1437 * while the dialog is not being closed), then destroy the dialog 1438 * box. 1439 */ 1440 case WM_ACTIVATE: 1441 { 1442 if (LOWORD(wParam) == WA_INACTIVE) 1443 { 1444 DestroyWindow(hwnd); 1445 PostQuitMessage(0); 1446 } 1447 return FALSE; 1448 } 1449 1450 case WM_COMMAND: 1451 switch (LOWORD(wParam)) 1452 { 1453 case IDC_LOG_OFF_BUTTON: 1454 case IDOK: 1455 ExitWindowsEx(EWX_LOGOFF, 0); 1456 break; 1457 1458 case IDCANCEL: 1459 DestroyWindow(hwnd); 1460 PostQuitMessage(IDCANCEL); 1461 break; 1462 } 1463 break; 1464 1465 case WM_DESTROY: 1466 DeleteObject(pContext->hBrush); 1467 DeleteObject(pContext->hImageStrip); 1468 DeleteObject(pContext->hfFont); 1469 1470 /* Remove the subclass from the buttons */ 1471 for (int i = 0; i < NUMBER_OF_BUTTONS; i++) 1472 { 1473 SetWindowLongPtrW(GetDlgItem(hwnd, IDC_LOG_OFF_BUTTON + i), GWLP_WNDPROC, (LONG_PTR)pContext->OldButtonProc); 1474 } 1475 return TRUE; 1476 1477 case WM_CTLCOLORSTATIC: 1478 { 1479 /* Either make background transparent or fill it with color for required static controls */ 1480 HDC hdcStatic = (HDC)wParam; 1481 UINT StaticID = (UINT)GetWindowLongPtrW((HWND)lParam, GWL_ID); 1482 1483 switch (StaticID) 1484 { 1485 case IDC_LOG_OFF_TEXT_STATIC: 1486 SetTextColor(hdcStatic, DARK_GREY_COLOR); 1487 SetBkMode(hdcStatic, TRANSPARENT); 1488 return (INT_PTR)GetStockObject(HOLLOW_BRUSH); 1489 1490 case IDC_LOG_OFF_STATIC: 1491 case IDC_SWITCH_USER_STATIC: 1492 SetTextColor(hdcStatic, LIGHT_GREY_COLOR); 1493 SetBkMode(hdcStatic, TRANSPARENT); 1494 return (LONG_PTR)pContext->hBrush; 1495 } 1496 return FALSE; 1497 } 1498 break; 1499 1500 case WM_DRAWITEM: 1501 { 1502 /* Draw bitmaps on required buttons */ 1503 switch (pdis->CtlID) 1504 { 1505 case IDC_LOG_OFF_BUTTON: 1506 case IDC_SWITCH_USER_BUTTON: 1507 return DrawIconOnOwnerDrawnButtons(pdis, pContext); 1508 } 1509 } 1510 break; 1511 1512 default: 1513 break; 1514 } 1515 return FALSE; 1516 } 1517 1518 /************************************************************************* 1519 * LogoffWindowsDialog [SHELL32.54] 1520 */ 1521 1522 EXTERN_C int WINAPI LogoffWindowsDialog(HWND hWndOwner) 1523 { 1524 CComPtr<IUnknown> fadeHandler; 1525 BOOL bIsAltKeyPressed = FALSE; 1526 MSG Msg; 1527 HWND parent = NULL; 1528 HWND hWndChild = NULL; 1529 WCHAR szBuffer[30]; 1530 DWORD LogoffDialogID = IDD_LOG_OFF; 1531 LOGOFF_DLG_CONTEXT Context; 1532 1533 if (!CallShellDimScreen(&fadeHandler, &parent)) 1534 parent = hWndOwner; 1535 1536 Context.bFriendlyUI = IsFriendlyUIActive(); 1537 if (Context.bFriendlyUI) 1538 { 1539 LogoffDialogID = IDD_LOG_OFF_FANCY; 1540 } 1541 1542 hWndChild = CreateDialogParamW(shell32_hInstance, MAKEINTRESOURCEW(LogoffDialogID), parent, LogOffDialogProc, (LPARAM)&Context); 1543 ShowWindow(hWndChild, SW_SHOWNORMAL); 1544 1545 /* Detect either Alt key has been pressed */ 1546 while (GetMessageW(&Msg, NULL, 0, 0)) 1547 { 1548 if(!IsDialogMessageW(hWndChild, &Msg)) 1549 { 1550 TranslateMessage(&Msg); 1551 DispatchMessageW(&Msg); 1552 } 1553 1554 switch (Msg.message) 1555 { 1556 case WM_SYSKEYDOWN: 1557 { 1558 /* If the Alt key has been pressed once, add prefix to static controls */ 1559 if (Msg.wParam == VK_MENU && !bIsAltKeyPressed && Context.bFriendlyUI) 1560 { 1561 for (int i = 0; i < NUMBER_OF_BUTTONS; i++) 1562 { 1563 GetDlgItemTextW(hWndChild, IDC_LOG_OFF_BUTTON + i, szBuffer, _countof(szBuffer)); 1564 SetDlgItemTextW(hWndChild, IDC_LOG_OFF_STATIC + i, szBuffer); 1565 } 1566 bIsAltKeyPressed = TRUE; 1567 } 1568 } 1569 break; 1570 } 1571 } 1572 return 0; 1573 } 1574 1575 /************************************************************************* 1576 * RestartDialog [SHELL32.59] 1577 */ 1578 1579 int WINAPI RestartDialog(HWND hWndOwner, LPCWSTR lpstrReason, DWORD uFlags) 1580 { 1581 return RestartDialogEx(hWndOwner, lpstrReason, uFlags, 0); 1582 } 1583 1584 /************************************************************************* 1585 * ExitWindowsDialog_backup 1586 * 1587 * NOTES 1588 * Used as a backup solution to shutdown the OS in case msgina.dll 1589 * somehow cannot be found. 1590 */ 1591 VOID ExitWindowsDialog_backup(HWND hWndOwner) 1592 { 1593 TRACE("(%p)\n", hWndOwner); 1594 1595 if (ConfirmDialog(hWndOwner, IDS_SHUTDOWN_PROMPT, IDS_SHUTDOWN_TITLE)) 1596 { 1597 EnablePrivilege(L"SeShutdownPrivilege", TRUE); 1598 ExitWindowsEx(EWX_SHUTDOWN, 0); 1599 EnablePrivilege(L"SeShutdownPrivilege", FALSE); 1600 } 1601 } 1602 1603 /************************************************************************* 1604 * ExitWindowsDialog [SHELL32.60] 1605 * 1606 * NOTES 1607 * exported by ordinal 1608 */ 1609 /* 1610 * TODO: 1611 * - Implement the ability to show either the Welcome Screen or the classic dialog boxes based upon the 1612 * registry value: SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\LogonType. 1613 */ 1614 void WINAPI ExitWindowsDialog(HWND hWndOwner) 1615 { 1616 typedef DWORD (WINAPI *ShellShFunc)(HWND hParent, WCHAR *Username, BOOL bHideLogoff); 1617 HINSTANCE msginaDll = LoadLibraryW(L"msgina.dll"); 1618 1619 TRACE("(%p)\n", hWndOwner); 1620 1621 CComPtr<IUnknown> fadeHandler; 1622 HWND parent; 1623 if (!CallShellDimScreen(&fadeHandler, &parent)) 1624 parent = hWndOwner; 1625 1626 /* If the DLL cannot be found for any reason, then it simply uses a 1627 dialog box to ask if the user wants to shut down the computer. */ 1628 if (!msginaDll) 1629 { 1630 TRACE("Unable to load msgina.dll.\n"); 1631 ExitWindowsDialog_backup(parent); 1632 return; 1633 } 1634 1635 ShellShFunc pShellShutdownDialog = (ShellShFunc)GetProcAddress(msginaDll, "ShellShutdownDialog"); 1636 1637 if (pShellShutdownDialog) 1638 { 1639 /* Actually call the function */ 1640 DWORD returnValue = pShellShutdownDialog(parent, NULL, FALSE); 1641 1642 switch (returnValue) 1643 { 1644 case 0x01: /* Log off user */ 1645 { 1646 ExitWindowsEx(EWX_LOGOFF, 0); 1647 break; 1648 } 1649 case 0x02: /* Shut down */ 1650 { 1651 EnablePrivilege(L"SeShutdownPrivilege", TRUE); 1652 ExitWindowsEx(EWX_SHUTDOWN, 0); 1653 EnablePrivilege(L"SeShutdownPrivilege", FALSE); 1654 break; 1655 } 1656 case 0x03: /* Install Updates/Shutdown (?) */ 1657 { 1658 break; 1659 } 1660 case 0x04: /* Reboot */ 1661 { 1662 EnablePrivilege(L"SeShutdownPrivilege", TRUE); 1663 ExitWindowsEx(EWX_REBOOT, 0); 1664 EnablePrivilege(L"SeShutdownPrivilege", FALSE); 1665 break; 1666 } 1667 case 0x10: /* Sleep */ 1668 { 1669 if (IsPwrSuspendAllowed()) 1670 { 1671 EnablePrivilege(L"SeShutdownPrivilege", TRUE); 1672 SetSuspendState(FALSE, FALSE, FALSE); 1673 EnablePrivilege(L"SeShutdownPrivilege", FALSE); 1674 } 1675 break; 1676 } 1677 case 0x40: /* Hibernate */ 1678 { 1679 if (IsPwrHibernateAllowed()) 1680 { 1681 EnablePrivilege(L"SeShutdownPrivilege", TRUE); 1682 SetSuspendState(TRUE, FALSE, TRUE); 1683 EnablePrivilege(L"SeShutdownPrivilege", FALSE); 1684 } 1685 break; 1686 } 1687 /* If the option is any other value */ 1688 default: 1689 break; 1690 } 1691 } 1692 else 1693 { 1694 /* If the function cannot be found, then revert to using the backup solution */ 1695 TRACE("Unable to find the 'ShellShutdownDialog' function"); 1696 ExitWindowsDialog_backup(parent); 1697 } 1698 1699 FreeLibrary(msginaDll); 1700 } 1701