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