1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Winlogon 4 * FILE: base/system/winlogon/shutdown.c 5 * PURPOSE: System shutdown dialog 6 * PROGRAMMERS: Edward Bronsten <tanki.alpha5056@gmail.com> 7 * Eric Kohl 8 * Hermes Belusca-Maito 9 */ 10 11 /* INCLUDES ******************************************************************/ 12 13 #include "winlogon.h" 14 15 #include <rpc.h> 16 #include <winreg_s.h> 17 18 /* DEFINES *******************************************************************/ 19 20 #define SHUTDOWN_TIMER_ID 2000 21 #define SECONDS_PER_DAY 86400 22 #define SECONDS_PER_DECADE 315360000 23 24 /* STRUCTS *******************************************************************/ 25 26 typedef struct _SYS_SHUTDOWN_PARAMS 27 { 28 PWSTR pszMessage; 29 ULONG dwTimeout; 30 31 HDESK hShutdownDesk; 32 WCHAR DesktopName[512]; 33 WINDOWPLACEMENT wpPos; 34 35 BOOLEAN bShuttingDown; 36 BOOLEAN bRebootAfterShutdown; 37 BOOLEAN bForceAppsClosed; 38 DWORD dwReason; 39 } SYS_SHUTDOWN_PARAMS, *PSYS_SHUTDOWN_PARAMS; 40 41 /* GLOBALS *******************************************************************/ 42 43 SYS_SHUTDOWN_PARAMS g_ShutdownParams; 44 45 /* FUNCTIONS *****************************************************************/ 46 47 static 48 BOOL 49 DoSystemShutdown( 50 IN PSYS_SHUTDOWN_PARAMS pShutdownParams) 51 { 52 BOOL Success; 53 54 /* If shutdown has been cancelled, bail out now */ 55 if (!pShutdownParams->bShuttingDown) 56 return TRUE; 57 58 Success = ExitWindowsEx((pShutdownParams->bRebootAfterShutdown ? EWX_REBOOT : EWX_SHUTDOWN) | 59 (pShutdownParams->bForceAppsClosed ? EWX_FORCE : 0), 60 pShutdownParams->dwReason); 61 if (!Success) 62 { 63 /* Something went wrong, cancel shutdown */ 64 pShutdownParams->bShuttingDown = FALSE; 65 } 66 67 return Success; 68 } 69 70 static 71 VOID 72 OnTimer( 73 IN HWND hwndDlg, 74 IN PSYS_SHUTDOWN_PARAMS pShutdownParams) 75 { 76 HDESK hInputDesktop; 77 BOOL bSuccess; 78 DWORD dwSize; 79 INT iSeconds, iMinutes, iHours, iDays; 80 WCHAR szFormatBuffer[32]; 81 WCHAR szBuffer[32]; 82 WCHAR DesktopName[512]; 83 84 if (!pShutdownParams->bShuttingDown) 85 { 86 /* Shutdown has been cancelled, close the dialog and bail out */ 87 EndDialog(hwndDlg, IDABORT); 88 return; 89 } 90 91 /* 92 * Check whether the input desktop has changed. If so, close the dialog, 93 * and let the shutdown thread recreate it on the new desktop. 94 */ 95 96 // TODO: Investigate: It would be great if we could also compare with 97 // our internally maintained desktop handles, before calling that heavy 98 // comparison. 99 // (Note that we cannot compare handles with arbitrary input desktop, 100 // since OpenInputDesktop() creates new handle instances everytime.) 101 102 hInputDesktop = OpenInputDesktop(0, FALSE, GENERIC_ALL); 103 if (!hInputDesktop) 104 { 105 /* No input desktop but we have a dialog: kill it */ 106 ERR("OpenInputDesktop() failed, error 0x%lx\n", GetLastError()); 107 EndDialog(hwndDlg, 0); 108 return; 109 } 110 bSuccess = GetUserObjectInformationW(hInputDesktop, 111 UOI_NAME, 112 DesktopName, 113 sizeof(DesktopName), 114 &dwSize); 115 if (!bSuccess) 116 { 117 ERR("GetUserObjectInformationW(0x%p) failed, error 0x%lx\n", 118 hInputDesktop, GetLastError()); 119 } 120 CloseDesktop(hInputDesktop); 121 122 if (bSuccess && (wcscmp(DesktopName, pShutdownParams->DesktopName) != 0)) 123 { 124 TRACE("Input desktop has changed: '%S' --> '%S'\n", 125 pShutdownParams->DesktopName, DesktopName); 126 127 /* Save the original dialog position to be restored later */ 128 pShutdownParams->wpPos.length = sizeof(pShutdownParams->wpPos); 129 GetWindowPlacement(hwndDlg, &pShutdownParams->wpPos); 130 131 /* Close the dialog */ 132 EndDialog(hwndDlg, IDCANCEL); 133 return; 134 } 135 136 /* Update the shutdown timeout */ 137 if (pShutdownParams->dwTimeout < SECONDS_PER_DAY) 138 { 139 iSeconds = (INT)pShutdownParams->dwTimeout; 140 iHours = iSeconds / 3600; 141 iSeconds -= iHours * 3600; 142 iMinutes = iSeconds / 60; 143 iSeconds -= iMinutes * 60; 144 145 LoadStringW(hAppInstance, IDS_TIMEOUTSHORTFORMAT, szFormatBuffer, ARRAYSIZE(szFormatBuffer)); 146 swprintf(szBuffer, szFormatBuffer, iHours, iMinutes, iSeconds); 147 } 148 else 149 { 150 iDays = (INT)(pShutdownParams->dwTimeout / SECONDS_PER_DAY); 151 152 LoadStringW(hAppInstance, IDS_TIMEOUTLONGFORMAT, szFormatBuffer, ARRAYSIZE(szFormatBuffer)); 153 swprintf(szBuffer, szFormatBuffer, iDays); 154 } 155 156 SetDlgItemTextW(hwndDlg, IDC_SYSSHUTDOWNTIMELEFT, szBuffer); 157 158 if (pShutdownParams->dwTimeout == 0) 159 { 160 /* Close the dialog and let the shutdown thread perform the system shutdown */ 161 EndDialog(hwndDlg, 0); 162 return; 163 } 164 165 pShutdownParams->dwTimeout--; 166 } 167 168 static 169 INT_PTR 170 CALLBACK 171 ShutdownDialogProc( 172 IN HWND hwndDlg, 173 IN UINT uMsg, 174 IN WPARAM wParam, 175 IN LPARAM lParam) 176 { 177 PSYS_SHUTDOWN_PARAMS pShutdownParams; 178 179 pShutdownParams = (PSYS_SHUTDOWN_PARAMS)GetWindowLongPtrW(hwndDlg, DWLP_USER); 180 181 switch (uMsg) 182 { 183 case WM_INITDIALOG: 184 { 185 pShutdownParams = (PSYS_SHUTDOWN_PARAMS)lParam; 186 SetWindowLongPtrW(hwndDlg, DWLP_USER, (LONG_PTR)pShutdownParams); 187 188 /* Display the shutdown message */ 189 if (pShutdownParams->pszMessage) 190 { 191 SetDlgItemTextW(hwndDlg, 192 IDC_SYSSHUTDOWNMESSAGE, 193 pShutdownParams->pszMessage); 194 } 195 196 /* Remove the Close menu item */ 197 DeleteMenu(GetSystemMenu(hwndDlg, FALSE), SC_CLOSE, MF_BYCOMMAND); 198 199 /* Position the window (initial position, or restore from old) */ 200 if (pShutdownParams->wpPos.length == sizeof(pShutdownParams->wpPos)) 201 SetWindowPlacement(hwndDlg, &pShutdownParams->wpPos); 202 203 SetWindowPos(hwndDlg, HWND_TOPMOST, 0, 0, 0, 0, 204 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); 205 206 /* Initialize the timer */ 207 PostMessageW(hwndDlg, WM_TIMER, 0, 0); 208 SetTimer(hwndDlg, SHUTDOWN_TIMER_ID, 1000, NULL); 209 break; 210 } 211 212 /* NOTE: Do not handle WM_CLOSE */ 213 case WM_DESTROY: 214 KillTimer(hwndDlg, SHUTDOWN_TIMER_ID); 215 break; 216 217 case WM_TIMER: 218 OnTimer(hwndDlg, pShutdownParams); 219 break; 220 221 default: 222 return FALSE; 223 } 224 225 return TRUE; 226 } 227 228 static 229 DWORD 230 WINAPI 231 InitiateSystemShutdownThread( 232 IN LPVOID lpParameter) 233 { 234 PSYS_SHUTDOWN_PARAMS pShutdownParams; 235 HDESK hInputDesktop; 236 DWORD dwSize; 237 INT_PTR res; 238 239 pShutdownParams = (PSYS_SHUTDOWN_PARAMS)lpParameter; 240 241 /* Default to initial dialog position */ 242 pShutdownParams->wpPos.length = 0; 243 244 /* Continuously display the shutdown dialog on the current input desktop */ 245 while (TRUE) 246 { 247 /* Retrieve the current input desktop */ 248 hInputDesktop = OpenInputDesktop(0, FALSE, GENERIC_ALL); 249 if (!hInputDesktop) 250 { 251 /* No input desktop on the current WinSta0, just shut down */ 252 ERR("OpenInputDesktop() failed, error 0x%lx\n", GetLastError()); 253 break; 254 } 255 256 /* Remember it for checking desktop changes later */ 257 pShutdownParams->hShutdownDesk = hInputDesktop; 258 if (!GetUserObjectInformationW(pShutdownParams->hShutdownDesk, 259 UOI_NAME, 260 pShutdownParams->DesktopName, 261 sizeof(pShutdownParams->DesktopName), 262 &dwSize)) 263 { 264 ERR("GetUserObjectInformationW(0x%p) failed, error 0x%lx\n", 265 pShutdownParams->hShutdownDesk, GetLastError()); 266 } 267 268 /* Assign the desktop to the current thread */ 269 SetThreadDesktop(hInputDesktop); 270 271 /* Display the shutdown dialog on the current input desktop */ 272 res = DialogBoxParamW(hAppInstance, 273 MAKEINTRESOURCEW(IDD_SYSSHUTDOWN), 274 NULL, 275 ShutdownDialogProc, 276 (LPARAM)pShutdownParams); 277 278 /* Close the desktop */ 279 CloseDesktop(hInputDesktop); 280 281 /* 282 * Check why the dialog has been closed. 283 * 284 * - If it failed to be created (returned -1), don't care about 285 * re-creating it, and proceed directly to shutdown. 286 * 287 * - If it closed unexpectedly (returned != 1), check whether a 288 * shutdown is in progress. If the shutdown has been cancelled, 289 * just bail out; if a shutdown is in progress and the timeout 290 * is 0, bail out and proceed to shutdown. 291 * 292 * - If the dialog has closed because the input desktop changed, 293 * loop again and recreate it on the new desktop. 294 */ 295 if ((res == -1) || (res != IDCANCEL) || 296 !(pShutdownParams->bShuttingDown && (pShutdownParams->dwTimeout > 0))) 297 { 298 break; 299 } 300 } 301 302 /* Reset dialog information */ 303 pShutdownParams->hShutdownDesk = NULL; 304 ZeroMemory(&pShutdownParams->DesktopName, sizeof(pShutdownParams->DesktopName)); 305 ZeroMemory(&pShutdownParams->wpPos, sizeof(pShutdownParams->wpPos)); 306 307 if (pShutdownParams->pszMessage) 308 { 309 HeapFree(GetProcessHeap(), 0, pShutdownParams->pszMessage); 310 pShutdownParams->pszMessage = NULL; 311 } 312 313 if (pShutdownParams->bShuttingDown) 314 { 315 /* Perform the system shutdown */ 316 if (DoSystemShutdown(pShutdownParams)) 317 return ERROR_SUCCESS; 318 else 319 return GetLastError(); 320 } 321 322 pShutdownParams->bShuttingDown = FALSE; 323 return ERROR_SUCCESS; 324 } 325 326 327 DWORD 328 TerminateSystemShutdown(VOID) 329 { 330 if (_InterlockedCompareExchange8((volatile char*)&g_ShutdownParams.bShuttingDown, FALSE, TRUE) == FALSE) 331 return ERROR_NO_SHUTDOWN_IN_PROGRESS; 332 333 return ERROR_SUCCESS; 334 } 335 336 DWORD 337 StartSystemShutdown( 338 IN PUNICODE_STRING pMessage, 339 IN ULONG dwTimeout, 340 IN BOOLEAN bForceAppsClosed, 341 IN BOOLEAN bRebootAfterShutdown, 342 IN ULONG dwReason) 343 { 344 HANDLE hThread; 345 346 /* Fail if the timeout is 10 years or more */ 347 if (dwTimeout >= SECONDS_PER_DECADE) 348 return ERROR_INVALID_PARAMETER; 349 350 if (_InterlockedCompareExchange8((volatile char*)&g_ShutdownParams.bShuttingDown, TRUE, FALSE) == TRUE) 351 return ERROR_SHUTDOWN_IN_PROGRESS; 352 353 if ((dwTimeout != 0) && pMessage && pMessage->Length && pMessage->Buffer) 354 { 355 g_ShutdownParams.pszMessage = HeapAlloc(GetProcessHeap(), 356 HEAP_ZERO_MEMORY, 357 pMessage->Length + sizeof(UNICODE_NULL)); 358 if (g_ShutdownParams.pszMessage == NULL) 359 { 360 g_ShutdownParams.bShuttingDown = FALSE; 361 return GetLastError(); 362 } 363 364 wcsncpy(g_ShutdownParams.pszMessage, 365 pMessage->Buffer, 366 pMessage->Length / sizeof(WCHAR)); 367 } 368 else 369 { 370 g_ShutdownParams.pszMessage = NULL; 371 } 372 373 g_ShutdownParams.dwTimeout = dwTimeout; 374 g_ShutdownParams.bForceAppsClosed = bForceAppsClosed; 375 g_ShutdownParams.bRebootAfterShutdown = bRebootAfterShutdown; 376 g_ShutdownParams.dwReason = dwReason; 377 378 /* If dwTimeout is zero perform an immediate system shutdown, 379 * otherwise display the countdown shutdown dialog. */ 380 if (g_ShutdownParams.dwTimeout == 0) 381 { 382 if (DoSystemShutdown(&g_ShutdownParams)) 383 return ERROR_SUCCESS; 384 } 385 else 386 { 387 hThread = CreateThread(NULL, 0, InitiateSystemShutdownThread, 388 &g_ShutdownParams, 0, NULL); 389 if (hThread) 390 { 391 CloseHandle(hThread); 392 return ERROR_SUCCESS; 393 } 394 } 395 396 if (g_ShutdownParams.pszMessage) 397 { 398 HeapFree(GetProcessHeap(), 0, g_ShutdownParams.pszMessage); 399 g_ShutdownParams.pszMessage = NULL; 400 } 401 402 g_ShutdownParams.bShuttingDown = FALSE; 403 return GetLastError(); 404 } 405 406 /* EOF */ 407