aboutsummaryrefslogtreecommitdiff
path: root/src/engine/uithread.cpp
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2018-12-29 22:12:08 -0600
committerSean Hall <r.sean.hall@gmail.com>2018-12-29 22:12:08 -0600
commit61847dddd4fd497057c780658e383c4627de19ec (patch)
treef85a845182922538ab9aa6ee85b0db3ab40c1f6e /src/engine/uithread.cpp
parent8295f5f8fd28042e1a0a172d5afbba79178064c2 (diff)
downloadwix-61847dddd4fd497057c780658e383c4627de19ec.tar.gz
wix-61847dddd4fd497057c780658e383c4627de19ec.tar.bz2
wix-61847dddd4fd497057c780658e383c4627de19ec.zip
Import code from old v4 repo
Diffstat (limited to 'src/engine/uithread.cpp')
-rw-r--r--src/engine/uithread.cpp220
1 files changed, 220 insertions, 0 deletions
diff --git a/src/engine/uithread.cpp b/src/engine/uithread.cpp
new file mode 100644
index 00000000..cf111745
--- /dev/null
+++ b/src/engine/uithread.cpp
@@ -0,0 +1,220 @@
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
10struct UITHREAD_CONTEXT
11{
12 HANDLE hInitializedEvent;
13 HINSTANCE hInstance;
14 BURN_ENGINE_STATE* pEngineState;
15};
16
17struct UITHREAD_INFO
18{
19 BOOL fElevated;
20 BURN_USER_EXPERIENCE* pUserExperience;
21};
22
23
24// internal function declarations
25
26static DWORD WINAPI ThreadProc(
27 __in LPVOID pvContext
28 );
29
30static 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
40HRESULT 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
68LExit:
69 ReleaseHandle(rgWaitHandles[1]);
70 ReleaseHandle(rgWaitHandles[0]);
71
72 return hr;
73}
74
75void 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
92static 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_TOOLWINDOW, wc.lpszClassName, NULL, WS_POPUP | WS_VISIBLE, CW_USEDEFAULT, SW_SHOWNA, 0, 0, HWND_DESKTOP, NULL, pContext->hInstance, &info);
138 ExitOnNullWithLastError(hWnd, hr, "Failed to create window.");
139
140 // Persist the window handle and let the caller know we've initialized.
141 pEngineState->hMessageWindow = hWnd;
142 ::SetEvent(pContext->hInitializedEvent);
143
144 // Pump messages until the window is closed.
145 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
146 {
147 if (-1 == fRet)
148 {
149 hr = E_UNEXPECTED;
150 ExitOnFailure(hr, "Unexpected return value from message pump.");
151 }
152 else if (!::IsDialogMessageW(msg.hwnd, &msg))
153 {
154 ::TranslateMessage(&msg);
155 ::DispatchMessageW(&msg);
156 }
157 }
158
159LExit:
160 if (fRegistered)
161 {
162 ::UnregisterClassW(BURN_UITHREAD_CLASS_WINDOW, pContext->hInstance);
163 }
164
165 return hr;
166}
167
168static LRESULT CALLBACK WndProc(
169 __in HWND hWnd,
170 __in UINT uMsg,
171 __in WPARAM wParam,
172 __in LPARAM lParam
173 )
174{
175 switch (uMsg)
176 {
177 case WM_NCCREATE:
178 {
179 LPCREATESTRUCTW lpcs = reinterpret_cast<LPCREATESTRUCTW>(lParam);
180 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(lpcs->lpCreateParams));
181 break;
182 }
183
184 case WM_NCDESTROY:
185 {
186 LRESULT lRes = ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
187 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
188 return lRes;
189 }
190
191 case WM_QUERYENDSESSION:
192 {
193 DWORD dwEndSession = static_cast<DWORD>(lParam);
194 BOOL fCritical = ENDSESSION_CRITICAL & dwEndSession;
195 BOOL fCancel = TRUE;
196 BOOL fRet = FALSE;
197
198 // Always block shutdown in the elevated process, but ask the BA in the non-elevated.
199 UITHREAD_INFO* pInfo = reinterpret_cast<UITHREAD_INFO*>(::GetWindowLongW(hWnd, GWLP_USERDATA));
200 if (!pInfo->fElevated)
201 {
202 // TODO: instead of recommending canceling all non-critical shutdowns, maybe we should only recommend cancel
203 // when the engine is doing work?
204 fCancel = !fCritical;
205 // TODO: There's a race condition here where the BA may not have been loaded, or already was unloaded.
206 UserExperienceOnSystemShutdown(pInfo->pUserExperience, dwEndSession, &fCancel);
207 }
208
209 fRet = !fCancel;
210 LogId(REPORT_STANDARD, MSG_SYSTEM_SHUTDOWN, LoggingBoolToString(fCritical), LoggingBoolToString(pInfo->fElevated), LoggingBoolToString(fRet));
211 return fRet;
212 }
213
214 case WM_DESTROY:
215 ::PostQuitMessage(0);
216 return 0;
217 }
218
219 return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
220}