diff options
Diffstat (limited to 'src/burn/engine/uithread.cpp')
-rw-r--r-- | src/burn/engine/uithread.cpp | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/src/burn/engine/uithread.cpp b/src/burn/engine/uithread.cpp new file mode 100644 index 00000000..433cb171 --- /dev/null +++ b/src/burn/engine/uithread.cpp | |||
@@ -0,0 +1,222 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
5 | #define BURN_UITHREAD_CLASS_WINDOW L"WixBurnMessageWindow" | ||
6 | |||
7 | |||
8 | // structs | ||
9 | |||
10 | struct UITHREAD_CONTEXT | ||
11 | { | ||
12 | HANDLE hInitializedEvent; | ||
13 | HINSTANCE hInstance; | ||
14 | BURN_ENGINE_STATE* pEngineState; | ||
15 | }; | ||
16 | |||
17 | struct UITHREAD_INFO | ||
18 | { | ||
19 | BOOL fElevated; | ||
20 | BURN_USER_EXPERIENCE* pUserExperience; | ||
21 | }; | ||
22 | |||
23 | |||
24 | // internal function declarations | ||
25 | |||
26 | static DWORD WINAPI ThreadProc( | ||
27 | __in LPVOID pvContext | ||
28 | ); | ||
29 | |||
30 | static LRESULT CALLBACK WndProc( | ||
31 | __in HWND hWnd, | ||
32 | __in UINT uMsg, | ||
33 | __in WPARAM wParam, | ||
34 | __in LPARAM lParam | ||
35 | ); | ||
36 | |||
37 | |||
38 | // function definitions | ||
39 | |||
40 | HRESULT UiCreateMessageWindow( | ||
41 | __in HINSTANCE hInstance, | ||
42 | __in BURN_ENGINE_STATE* pEngineState | ||
43 | ) | ||
44 | { | ||
45 | HRESULT hr = S_OK; | ||
46 | HANDLE rgWaitHandles[2] = { }; | ||
47 | UITHREAD_CONTEXT context = { }; | ||
48 | |||
49 | // Create event to signal after the UI thread / window is initialized. | ||
50 | rgWaitHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL); | ||
51 | ExitOnNullWithLastError(rgWaitHandles[0], hr, "Failed to create initialization event."); | ||
52 | |||
53 | // Pass necessary information to create the window. | ||
54 | context.hInitializedEvent = rgWaitHandles[0]; | ||
55 | context.hInstance = hInstance; | ||
56 | context.pEngineState = pEngineState; | ||
57 | |||
58 | // Create our separate UI thread. | ||
59 | rgWaitHandles[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, NULL); | ||
60 | ExitOnNullWithLastError(rgWaitHandles[1], hr, "Failed to create the UI thread."); | ||
61 | |||
62 | // Wait for either the thread to be initialized or the window to exit / fail prematurely. | ||
63 | ::WaitForMultipleObjects(countof(rgWaitHandles), rgWaitHandles, FALSE, INFINITE); | ||
64 | |||
65 | pEngineState->hMessageWindowThread = rgWaitHandles[1]; | ||
66 | rgWaitHandles[1] = NULL; | ||
67 | |||
68 | LExit: | ||
69 | ReleaseHandle(rgWaitHandles[1]); | ||
70 | ReleaseHandle(rgWaitHandles[0]); | ||
71 | |||
72 | return hr; | ||
73 | } | ||
74 | |||
75 | void UiCloseMessageWindow( | ||
76 | __in BURN_ENGINE_STATE* pEngineState | ||
77 | ) | ||
78 | { | ||
79 | if (::IsWindow(pEngineState->hMessageWindow)) | ||
80 | { | ||
81 | ::PostMessageW(pEngineState->hMessageWindow, WM_CLOSE, 0, 0); | ||
82 | |||
83 | // Give the window 15 seconds to close because if it stays open it can prevent | ||
84 | // the engine from starting a reboot (should a reboot actually be necessary). | ||
85 | ::WaitForSingleObject(pEngineState->hMessageWindowThread, 15 * 1000); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | |||
90 | // internal function definitions | ||
91 | |||
92 | static DWORD WINAPI ThreadProc( | ||
93 | __in LPVOID pvContext | ||
94 | ) | ||
95 | { | ||
96 | HRESULT hr = S_OK; | ||
97 | UITHREAD_CONTEXT* pContext = static_cast<UITHREAD_CONTEXT*>(pvContext); | ||
98 | UITHREAD_INFO info = { }; | ||
99 | |||
100 | WNDCLASSW wc = { }; | ||
101 | BOOL fRegistered = TRUE; | ||
102 | HWND hWnd = NULL; | ||
103 | |||
104 | BOOL fRet = FALSE; | ||
105 | MSG msg = { }; | ||
106 | |||
107 | BURN_ENGINE_STATE* pEngineState = pContext->pEngineState; | ||
108 | BOOL fElevated = BURN_MODE_ELEVATED == pContext->pEngineState->mode; | ||
109 | |||
110 | // If elevated, set up the thread local storage to store the correct pipe to communicate logging. | ||
111 | if (fElevated) | ||
112 | { | ||
113 | Assert(TLS_OUT_OF_INDEXES != pEngineState->dwElevatedLoggingTlsId); | ||
114 | |||
115 | if (!::TlsSetValue(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe)) | ||
116 | { | ||
117 | // If the function failed we cannot write to the pipe so just terminate. | ||
118 | ExitFunction1(hr = E_INVALIDSTATE); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | wc.lpfnWndProc = WndProc; | ||
123 | wc.hInstance = pContext->hInstance; | ||
124 | wc.lpszClassName = BURN_UITHREAD_CLASS_WINDOW; | ||
125 | |||
126 | if (!::RegisterClassW(&wc)) | ||
127 | { | ||
128 | ExitWithLastError(hr, "Failed to register window."); | ||
129 | } | ||
130 | |||
131 | fRegistered = TRUE; | ||
132 | |||
133 | info.fElevated = fElevated; | ||
134 | info.pUserExperience = &pEngineState->userExperience; | ||
135 | |||
136 | // Create the window to handle reboots without activating it. | ||
137 | hWnd = ::CreateWindowExW(WS_EX_NOACTIVATE, wc.lpszClassName, NULL, WS_POPUP, 0, 0, 0, 0, HWND_DESKTOP, NULL, pContext->hInstance, &info); | ||
138 | ExitOnNullWithLastError(hWnd, hr, "Failed to create window."); | ||
139 | |||
140 | ::ShowWindow(hWnd, SW_SHOWNA); | ||
141 | |||
142 | // Persist the window handle and let the caller know we've initialized. | ||
143 | pEngineState->hMessageWindow = hWnd; | ||
144 | ::SetEvent(pContext->hInitializedEvent); | ||
145 | |||
146 | // Pump messages until the window is closed. | ||
147 | while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) | ||
148 | { | ||
149 | if (-1 == fRet) | ||
150 | { | ||
151 | hr = E_UNEXPECTED; | ||
152 | ExitOnFailure(hr, "Unexpected return value from message pump."); | ||
153 | } | ||
154 | else if (!::IsDialogMessageW(msg.hwnd, &msg)) | ||
155 | { | ||
156 | ::TranslateMessage(&msg); | ||
157 | ::DispatchMessageW(&msg); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | LExit: | ||
162 | if (fRegistered) | ||
163 | { | ||
164 | ::UnregisterClassW(BURN_UITHREAD_CLASS_WINDOW, pContext->hInstance); | ||
165 | } | ||
166 | |||
167 | return hr; | ||
168 | } | ||
169 | |||
170 | static LRESULT CALLBACK WndProc( | ||
171 | __in HWND hWnd, | ||
172 | __in UINT uMsg, | ||
173 | __in WPARAM wParam, | ||
174 | __in LPARAM lParam | ||
175 | ) | ||
176 | { | ||
177 | switch (uMsg) | ||
178 | { | ||
179 | case WM_NCCREATE: | ||
180 | { | ||
181 | LPCREATESTRUCTW lpcs = reinterpret_cast<LPCREATESTRUCTW>(lParam); | ||
182 | ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(lpcs->lpCreateParams)); | ||
183 | break; | ||
184 | } | ||
185 | |||
186 | case WM_NCDESTROY: | ||
187 | { | ||
188 | LRESULT lRes = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); | ||
189 | ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); | ||
190 | return lRes; | ||
191 | } | ||
192 | |||
193 | case WM_QUERYENDSESSION: | ||
194 | { | ||
195 | DWORD dwEndSession = static_cast<DWORD>(lParam); | ||
196 | BOOL fCritical = ENDSESSION_CRITICAL & dwEndSession; | ||
197 | BOOL fCancel = TRUE; | ||
198 | BOOL fRet = FALSE; | ||
199 | |||
200 | // Always block shutdown in the elevated process, but ask the BA in the non-elevated. | ||
201 | UITHREAD_INFO* pInfo = reinterpret_cast<UITHREAD_INFO*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); | ||
202 | if (!pInfo->fElevated) | ||
203 | { | ||
204 | // TODO: instead of recommending canceling all non-critical shutdowns, maybe we should only recommend cancel | ||
205 | // when the engine is doing work? | ||
206 | fCancel = !fCritical; | ||
207 | // TODO: There's a race condition here where the BA may not have been loaded, or already was unloaded. | ||
208 | UserExperienceOnSystemShutdown(pInfo->pUserExperience, dwEndSession, &fCancel); | ||
209 | } | ||
210 | |||
211 | fRet = !fCancel; | ||
212 | LogId(REPORT_STANDARD, MSG_SYSTEM_SHUTDOWN, LoggingBoolToString(fCritical), LoggingBoolToString(pInfo->fElevated), LoggingBoolToString(fRet)); | ||
213 | return fRet; | ||
214 | } | ||
215 | |||
216 | case WM_DESTROY: | ||
217 | ::PostQuitMessage(0); | ||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); | ||
222 | } | ||