1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS msgina.dll 4 * FILE: lib/msgina/shutdown.c 5 * PURPOSE: Shutdown Dialog Box (GUI only) 6 * PROGRAMMERS: Lee Schroeder (spaceseel at gmail dot com) 7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr) 8 */ 9 10 #include "msgina.h" 11 #include <powrprof.h> 12 #include <wingdi.h> 13 14 /* Shutdown state flags */ 15 #define WLX_SHUTDOWN_STATE_LOGOFF 0x01 16 #define WLX_SHUTDOWN_STATE_POWER_OFF 0x02 17 #define WLX_SHUTDOWN_STATE_REBOOT 0x04 18 // 0x08 19 #define WLX_SHUTDOWN_STATE_SLEEP 0x10 20 // 0x20 21 #define WLX_SHUTDOWN_STATE_HIBERNATE 0x40 22 // 0x80 23 24 typedef struct _SHUTDOWN_DLG_CONTEXT 25 { 26 PGINA_CONTEXT pgContext; 27 HBITMAP hBitmap; 28 DWORD ShutdownOptions; 29 BOOL bCloseDlg; 30 BOOL bReasonUI; 31 } SHUTDOWN_DLG_CONTEXT, *PSHUTDOWN_DLG_CONTEXT; 32 33 34 static 35 BOOL 36 GetShutdownReasonUI(VOID) 37 { 38 OSVERSIONINFOEX VersionInfo; 39 DWORD dwValue, dwSize; 40 HKEY hKey; 41 LONG lRet; 42 43 /* Query the policy value */ 44 lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 45 L"Software\\Policies\\Microsoft\\Windows NT\\Reliability", 46 0, 47 KEY_QUERY_VALUE, 48 &hKey); 49 if (lRet == ERROR_SUCCESS) 50 { 51 dwValue = 0; 52 dwSize = sizeof(dwValue); 53 RegQueryValueExW(hKey, 54 L"ShutdownReasonUI", 55 NULL, 56 NULL, 57 (LPBYTE)&dwValue, 58 &dwSize); 59 RegCloseKey(hKey); 60 61 return (dwValue != 0) ? TRUE : FALSE; 62 } 63 64 /* Query the machine value */ 65 lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 66 L"Software\\Microsoft\\Windows\\CurrentVersion\\Reliability", 67 0, 68 KEY_QUERY_VALUE, 69 &hKey); 70 if (lRet == ERROR_SUCCESS) 71 { 72 dwValue = 0; 73 dwSize = sizeof(dwValue); 74 RegQueryValueExW(hKey, 75 L"ShutdownReasonUI", 76 NULL, 77 NULL, 78 (LPBYTE)&dwValue, 79 &dwSize); 80 RegCloseKey(hKey); 81 82 return (dwValue != 0) ? TRUE : FALSE; 83 } 84 85 /* Return the default value */ 86 VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo); 87 if (!GetVersionEx((POSVERSIONINFO)&VersionInfo)) 88 return FALSE; 89 90 return FALSE; 91 // return (VersionInfo.wProductType == VER_NT_WORKSTATION) ? FALSE : TRUE; 92 } 93 94 DWORD 95 GetDefaultShutdownSelState(VOID) 96 { 97 return WLX_SAS_ACTION_SHUTDOWN_POWER_OFF; 98 } 99 100 DWORD 101 LoadShutdownSelState(VOID) 102 { 103 LONG lRet; 104 HKEY hKeyCurrentUser, hKey; 105 DWORD dwValue, dwTemp, dwSize; 106 107 /* Default to shutdown */ 108 dwValue = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF; 109 110 /* Open the current user HKCU key */ 111 lRet = RegOpenCurrentUser(MAXIMUM_ALLOWED, &hKeyCurrentUser); 112 if (lRet == ERROR_SUCCESS) 113 { 114 /* Open the subkey */ 115 lRet = RegOpenKeyExW(hKeyCurrentUser, 116 L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", 117 0, KEY_QUERY_VALUE, &hKey); 118 RegCloseKey(hKeyCurrentUser); 119 } 120 if (lRet != ERROR_SUCCESS) 121 return dwValue; 122 123 /* Read the value */ 124 dwSize = sizeof(dwTemp); 125 lRet = RegQueryValueExW(hKey, 126 L"Shutdown Setting", 127 NULL, NULL, 128 (LPBYTE)&dwTemp, &dwSize); 129 RegCloseKey(hKey); 130 131 if (lRet == ERROR_SUCCESS) 132 { 133 switch (dwTemp) 134 { 135 case WLX_SHUTDOWN_STATE_LOGOFF: 136 dwValue = WLX_SAS_ACTION_LOGOFF; 137 break; 138 139 case WLX_SHUTDOWN_STATE_POWER_OFF: 140 dwValue = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF; 141 break; 142 143 case WLX_SHUTDOWN_STATE_REBOOT: 144 dwValue = WLX_SAS_ACTION_SHUTDOWN_REBOOT; 145 break; 146 147 // 0x08 148 149 case WLX_SHUTDOWN_STATE_SLEEP: 150 dwValue = WLX_SAS_ACTION_SHUTDOWN_SLEEP; 151 break; 152 153 // 0x20 154 155 case WLX_SHUTDOWN_STATE_HIBERNATE: 156 dwValue = WLX_SAS_ACTION_SHUTDOWN_HIBERNATE; 157 break; 158 159 // 0x80 160 } 161 } 162 163 return dwValue; 164 } 165 166 VOID 167 SaveShutdownSelState( 168 IN DWORD ShutdownCode) 169 { 170 LONG lRet; 171 HKEY hKeyCurrentUser, hKey; 172 DWORD dwValue = 0; 173 174 /* Open the current user HKCU key */ 175 lRet = RegOpenCurrentUser(MAXIMUM_ALLOWED, &hKeyCurrentUser); 176 if (lRet == ERROR_SUCCESS) 177 { 178 /* Create the subkey */ 179 lRet = RegCreateKeyExW(hKeyCurrentUser, 180 L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", 181 0, NULL, 182 REG_OPTION_NON_VOLATILE, 183 KEY_SET_VALUE, 184 NULL, &hKey, NULL); 185 RegCloseKey(hKeyCurrentUser); 186 } 187 if (lRet != ERROR_SUCCESS) 188 return; 189 190 switch (ShutdownCode) 191 { 192 case WLX_SAS_ACTION_LOGOFF: 193 dwValue = WLX_SHUTDOWN_STATE_LOGOFF; 194 break; 195 196 case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF: 197 dwValue = WLX_SHUTDOWN_STATE_POWER_OFF; 198 break; 199 200 case WLX_SAS_ACTION_SHUTDOWN_REBOOT: 201 dwValue = WLX_SHUTDOWN_STATE_REBOOT; 202 break; 203 204 case WLX_SAS_ACTION_SHUTDOWN_SLEEP: 205 dwValue = WLX_SHUTDOWN_STATE_SLEEP; 206 break; 207 208 case WLX_SAS_ACTION_SHUTDOWN_HIBERNATE: 209 dwValue = WLX_SHUTDOWN_STATE_HIBERNATE; 210 break; 211 } 212 213 RegSetValueExW(hKey, 214 L"Shutdown Setting", 215 0, REG_DWORD, 216 (LPBYTE)&dwValue, sizeof(dwValue)); 217 RegCloseKey(hKey); 218 } 219 220 DWORD 221 GetDefaultShutdownOptions(VOID) 222 { 223 return WLX_SHUTDOWN_STATE_POWER_OFF | WLX_SHUTDOWN_STATE_REBOOT; 224 } 225 226 DWORD 227 GetAllowedShutdownOptions(VOID) 228 { 229 DWORD Options = 0; 230 231 // FIXME: Compute those options accordings to current user's rights! 232 Options |= WLX_SHUTDOWN_STATE_LOGOFF | WLX_SHUTDOWN_STATE_POWER_OFF | WLX_SHUTDOWN_STATE_REBOOT; 233 234 if (IsPwrSuspendAllowed()) 235 Options |= WLX_SHUTDOWN_STATE_SLEEP; 236 237 if (IsPwrHibernateAllowed()) 238 Options |= WLX_SHUTDOWN_STATE_HIBERNATE; 239 240 return Options; 241 } 242 243 static VOID 244 UpdateShutdownDesc( 245 IN HWND hDlg, 246 IN PSHUTDOWN_DLG_CONTEXT pContext) // HINSTANCE hInstance 247 { 248 UINT DescId = 0; 249 DWORD ShutdownCode; 250 WCHAR szBuffer[256]; 251 252 ShutdownCode = SendDlgItemMessageW(hDlg, IDC_SHUTDOWN_ACTION, CB_GETCURSEL, 0, 0); 253 if (ShutdownCode == CB_ERR) // Invalid selection 254 return; 255 256 ShutdownCode = SendDlgItemMessageW(hDlg, IDC_SHUTDOWN_ACTION, CB_GETITEMDATA, ShutdownCode, 0); 257 258 switch (ShutdownCode) 259 { 260 case WLX_SAS_ACTION_LOGOFF: 261 DescId = IDS_SHUTDOWN_LOGOFF_DESC; 262 break; 263 264 case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF: 265 DescId = IDS_SHUTDOWN_SHUTDOWN_DESC; 266 break; 267 268 case WLX_SAS_ACTION_SHUTDOWN_REBOOT: 269 DescId = IDS_SHUTDOWN_RESTART_DESC; 270 break; 271 272 case WLX_SAS_ACTION_SHUTDOWN_SLEEP: 273 DescId = IDS_SHUTDOWN_SLEEP_DESC; 274 break; 275 276 case WLX_SAS_ACTION_SHUTDOWN_HIBERNATE: 277 DescId = IDS_SHUTDOWN_HIBERNATE_DESC; 278 break; 279 280 default: 281 break; 282 } 283 284 LoadStringW(pContext->pgContext->hDllInstance, DescId, szBuffer, _countof(szBuffer)); 285 SetDlgItemTextW(hDlg, IDC_SHUTDOWN_DESCRIPTION, szBuffer); 286 287 if (pContext->bReasonUI) 288 { 289 EnableWindow(GetDlgItem(hDlg, IDC_REASON_PLANNED), (ShutdownCode != WLX_SAS_ACTION_LOGOFF)); 290 EnableWindow(GetDlgItem(hDlg, IDC_REASON_LIST), (ShutdownCode != WLX_SAS_ACTION_LOGOFF)); 291 EnableWindow(GetDlgItem(hDlg, IDC_REASON_COMMENT), (ShutdownCode != WLX_SAS_ACTION_LOGOFF)); 292 } 293 } 294 295 static VOID 296 ShutdownOnInit( 297 IN HWND hDlg, 298 IN PSHUTDOWN_DLG_CONTEXT pContext) 299 { 300 PGINA_CONTEXT pgContext = pContext->pgContext; 301 HWND hwndList; 302 INT idx, count, i; 303 WCHAR szBuffer[256]; 304 WCHAR szBuffer2[256]; 305 306 hwndList = GetDlgItem(hDlg, IDC_SHUTDOWN_ACTION); 307 308 /* Clear the content before it's used */ 309 SendMessageW(hwndList, CB_RESETCONTENT, 0, 0); 310 311 /* Log off */ 312 if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_LOGOFF) 313 { 314 LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_LOGOFF, szBuffer, _countof(szBuffer)); 315 StringCchPrintfW(szBuffer2, _countof(szBuffer2), szBuffer, pgContext->UserName); 316 idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer2); 317 if (idx != CB_ERR) 318 SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_LOGOFF); 319 } 320 321 /* Shut down - DEFAULT */ 322 if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_POWER_OFF) 323 { 324 LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_SHUTDOWN, szBuffer, _countof(szBuffer)); 325 idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer); 326 if (idx != CB_ERR) 327 SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_POWER_OFF); 328 } 329 330 /* Restart */ 331 if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_REBOOT) 332 { 333 LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_RESTART, szBuffer, _countof(szBuffer)); 334 idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer); 335 if (idx != CB_ERR) 336 SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_REBOOT); 337 } 338 339 // if (pContext->ShutdownOptions & 0x08) {} 340 341 /* Sleep */ 342 if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_SLEEP) 343 { 344 LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_SLEEP, szBuffer, _countof(szBuffer)); 345 idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer); 346 if (idx != CB_ERR) 347 SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_SLEEP); 348 } 349 350 // if (pContext->ShutdownOptions & 0x20) {} 351 352 /* Hibernate */ 353 if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_HIBERNATE) 354 { 355 LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_HIBERNATE, szBuffer, _countof(szBuffer)); 356 idx = SendMessageW(hwndList, CB_ADDSTRING, 0, (LPARAM)szBuffer); 357 if (idx != CB_ERR) 358 SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_HIBERNATE); 359 } 360 361 // if (pContext->ShutdownOptions & 0x80) {} 362 363 /* Set the default shut down selection */ 364 count = SendMessageW(hwndList, CB_GETCOUNT, 0, 0); 365 for (i = 0; i < count; i++) 366 { 367 if (SendMessageW(hwndList, CB_GETITEMDATA, i, 0) == pgContext->nShutdownAction) 368 { 369 SendMessageW(hwndList, CB_SETCURSEL, i, 0); 370 break; 371 } 372 } 373 374 /* Update the choice description based on the current selection */ 375 UpdateShutdownDesc(hDlg, pContext); 376 } 377 378 static VOID 379 ShutdownOnOk( 380 IN HWND hDlg, 381 IN PGINA_CONTEXT pgContext) 382 { 383 INT idx; 384 385 idx = SendDlgItemMessageW(hDlg, 386 IDC_SHUTDOWN_ACTION, 387 CB_GETCURSEL, 388 0, 389 0); 390 if (idx != CB_ERR) 391 { 392 pgContext->nShutdownAction = 393 SendDlgItemMessageW(hDlg, 394 IDC_SHUTDOWN_ACTION, 395 CB_GETITEMDATA, 396 idx, 397 0); 398 } 399 } 400 401 static INT_PTR 402 CALLBACK 403 ShutdownDialogProc( 404 HWND hDlg, 405 UINT uMsg, 406 WPARAM wParam, 407 LPARAM lParam) 408 { 409 PSHUTDOWN_DLG_CONTEXT pContext; 410 411 pContext = (PSHUTDOWN_DLG_CONTEXT)GetWindowLongPtrW(hDlg, GWLP_USERDATA); 412 413 switch (uMsg) 414 { 415 case WM_INITDIALOG: 416 { 417 pContext = (PSHUTDOWN_DLG_CONTEXT)lParam; 418 SetWindowLongPtrW(hDlg, GWLP_USERDATA, (LONG_PTR)pContext); 419 420 ShutdownOnInit(hDlg, pContext); 421 422 /* Draw the logo bitmap */ 423 pContext->hBitmap = 424 LoadImageW(pContext->pgContext->hDllInstance, MAKEINTRESOURCEW(IDI_ROSLOGO), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR); 425 return TRUE; 426 } 427 428 case WM_DESTROY: 429 DeleteObject(pContext->hBitmap); 430 return TRUE; 431 432 case WM_ACTIVATE: 433 { 434 /* 435 * If the user deactivates the shutdown dialog (it loses its focus 436 * while the dialog is not being closed), then destroy the dialog 437 * and cancel shutdown. 438 */ 439 if (LOWORD(wParam) == WA_INACTIVE) 440 { 441 if (!pContext->bCloseDlg) 442 { 443 pContext->bCloseDlg = TRUE; 444 EndDialog(hDlg, 0); 445 } 446 } 447 return FALSE; 448 } 449 450 case WM_PAINT: 451 { 452 PAINTSTRUCT ps; 453 if (pContext->hBitmap) 454 { 455 BeginPaint(hDlg, &ps); 456 DrawStateW(ps.hdc, NULL, NULL, (LPARAM)pContext->hBitmap, (WPARAM)0, 0, 0, 0, 0, DST_BITMAP); 457 EndPaint(hDlg, &ps); 458 } 459 return TRUE; 460 } 461 462 case WM_CLOSE: 463 pContext->bCloseDlg = TRUE; 464 EndDialog(hDlg, IDCANCEL); 465 break; 466 467 case WM_COMMAND: 468 switch (LOWORD(wParam)) 469 { 470 case IDOK: 471 ShutdownOnOk(hDlg, pContext->pgContext); 472 473 /* Fall back */ 474 case IDCANCEL: 475 case IDHELP: 476 pContext->bCloseDlg = TRUE; 477 EndDialog(hDlg, LOWORD(wParam)); 478 break; 479 480 case IDC_SHUTDOWN_ACTION: 481 UpdateShutdownDesc(hDlg, pContext); 482 break; 483 } 484 break; 485 486 default: 487 return FALSE; 488 } 489 return TRUE; 490 } 491 492 INT_PTR 493 ShutdownDialog( 494 IN HWND hwndDlg, 495 IN DWORD ShutdownOptions, 496 IN PGINA_CONTEXT pgContext) 497 { 498 INT_PTR ret; 499 SHUTDOWN_DLG_CONTEXT Context; 500 501 #if 0 502 DWORD ShutdownOptions; 503 504 // FIXME: User impersonation!! 505 pgContext->nShutdownAction = LoadShutdownSelState(); 506 ShutdownOptions = GetAllowedShutdownOptions(); 507 #endif 508 509 Context.pgContext = pgContext; 510 Context.ShutdownOptions = ShutdownOptions; 511 Context.bCloseDlg = FALSE; 512 Context.bReasonUI = GetShutdownReasonUI(); 513 514 if (pgContext->hWlx && pgContext->pWlxFuncs) 515 { 516 ret = pgContext->pWlxFuncs->WlxDialogBoxParam(pgContext->hWlx, 517 pgContext->hDllInstance, 518 MAKEINTRESOURCEW(Context.bReasonUI ? IDD_SHUTDOWN_REASON : IDD_SHUTDOWN), 519 hwndDlg, 520 ShutdownDialogProc, 521 (LPARAM)&Context); 522 } 523 else 524 { 525 ret = DialogBoxParamW(pgContext->hDllInstance, 526 MAKEINTRESOURCEW(Context.bReasonUI ? IDD_SHUTDOWN_REASON : IDD_SHUTDOWN), 527 hwndDlg, 528 ShutdownDialogProc, 529 (LPARAM)&Context); 530 } 531 532 #if 0 533 // FIXME: User impersonation!! 534 if (ret == IDOK) 535 SaveShutdownSelState(pgContext->nShutdownAction); 536 #endif 537 538 return ret; 539 } 540 541 542 /* 543 * NOTES: 544 * - Based upon observations on the ShellShutdownDialog() function, the function doesn't actually 545 * do anything except show a dialog box and returning a value based upon the value chosen. That 546 * means that any code that calls the function has to execute the chosen action (shut down, 547 * restart, etc.). 548 * - When this function is called in Windows XP, it shows the classic dialog box regardless if 549 * SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\LogonType is enabled or not. 550 * - When the Help button is pushed, it sends the same return value as IDCANCEL (0x00), but 551 * at the same time, it calls the help file directly from the dialog box. 552 * - When the dialog is created, it doesn't disable all other input from the other windows. 553 * This is done elsewhere. When running the function ShellShutdownDialog() from XP/2K3, if the user clicks 554 * out of the window, it automatically closes itself. 555 * - The parameter, lpUsername never seems to be used when calling the function from Windows XP. Either 556 * it was a parameter that was never used in the final version before release, or it has a use that 557 * is currently not known. 558 */ 559 DWORD WINAPI 560 ShellShutdownDialog( 561 HWND hParent, 562 LPWSTR lpUsername, 563 BOOL bHideLogoff) 564 { 565 INT_PTR dlgValue; 566 DWORD ShutdownOptions; 567 568 /* 569 * As we are called by the shell itself, don't use 570 * the cached GINA context but use a local copy here. 571 */ 572 GINA_CONTEXT gContext = { 0 }; 573 DWORD BufferSize; 574 575 UNREFERENCED_PARAMETER(lpUsername); 576 577 ShutdownOptions = GetAllowedShutdownOptions(); 578 if (bHideLogoff) 579 ShutdownOptions &= ~WLX_SHUTDOWN_STATE_LOGOFF; 580 581 /* Initialize our local GINA context */ 582 gContext.hDllInstance = hDllInstance; 583 BufferSize = _countof(gContext.UserName); 584 // NOTE: Only when this function is called, Win checks inside 585 // HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", 586 // value "Logon User Name", and determines whether it will display 587 // the user name. 588 GetUserNameW(gContext.UserName, &BufferSize); 589 gContext.nShutdownAction = LoadShutdownSelState(); 590 591 /* Load the shutdown dialog box */ 592 dlgValue = ShutdownDialog(hParent, ShutdownOptions, &gContext); 593 594 /* Determine what to do based on user selection */ 595 if (dlgValue == IDOK) 596 { 597 SaveShutdownSelState(gContext.nShutdownAction); 598 599 switch (gContext.nShutdownAction) 600 { 601 case WLX_SAS_ACTION_LOGOFF: 602 return WLX_SHUTDOWN_STATE_LOGOFF; 603 604 case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF: 605 return WLX_SHUTDOWN_STATE_POWER_OFF; 606 607 case WLX_SAS_ACTION_SHUTDOWN_REBOOT: 608 return WLX_SHUTDOWN_STATE_REBOOT; 609 610 // 0x08 611 612 case WLX_SAS_ACTION_SHUTDOWN_SLEEP: 613 return WLX_SHUTDOWN_STATE_SLEEP; 614 615 // 0x20 616 617 case WLX_SAS_ACTION_SHUTDOWN_HIBERNATE: 618 return WLX_SHUTDOWN_STATE_HIBERNATE; 619 620 // 0x80 621 } 622 } 623 /* Help file is called directly here */ 624 else if (dlgValue == IDHELP) 625 { 626 FIXME("Help is not implemented yet."); 627 MessageBoxW(hParent, L"Help is not implemented yet.", L"Message", MB_OK | MB_ICONEXCLAMATION); 628 } 629 else if (dlgValue == -1) 630 { 631 ERR("Failed to create dialog\n"); 632 } 633 634 return 0; 635 } 636