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
DoSystemShutdown(IN PSYS_SHUTDOWN_PARAMS pShutdownParams)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
OnTimer(IN HWND hwndDlg,IN PSYS_SHUTDOWN_PARAMS pShutdownParams)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
ShutdownDialogProc(IN HWND hwndDlg,IN UINT uMsg,IN WPARAM wParam,IN LPARAM lParam)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
InitiateSystemShutdownThread(IN LPVOID lpParameter)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
TerminateSystemShutdown(VOID)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
StartSystemShutdown(IN PUNICODE_STRING pMessage,IN ULONG dwTimeout,IN BOOLEAN bForceAppsClosed,IN BOOLEAN bRebootAfterShutdown,IN ULONG dwReason)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