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