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