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 /* For bIsButtonHot */ 1128 #define LOG_OFF_BUTTON_HOT 0 1129 #define SWITCH_USER_BUTTON_HOT 1 1130 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 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 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 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 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 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 */ 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 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 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 */ 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 */ 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