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 | } | ||
