diff options
| author | Sean Hall <r.sean.hall@gmail.com> | 2018-12-29 22:12:08 -0600 |
|---|---|---|
| committer | Sean Hall <r.sean.hall@gmail.com> | 2018-12-29 22:12:08 -0600 |
| commit | 61847dddd4fd497057c780658e383c4627de19ec (patch) | |
| tree | f85a845182922538ab9aa6ee85b0db3ab40c1f6e /src/engine/splashscreen.cpp | |
| parent | 8295f5f8fd28042e1a0a172d5afbba79178064c2 (diff) | |
| download | wix-61847dddd4fd497057c780658e383c4627de19ec.tar.gz wix-61847dddd4fd497057c780658e383c4627de19ec.tar.bz2 wix-61847dddd4fd497057c780658e383c4627de19ec.zip | |
Import code from old v4 repo
Diffstat (limited to 'src/engine/splashscreen.cpp')
| -rw-r--r-- | src/engine/splashscreen.cpp | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/src/engine/splashscreen.cpp b/src/engine/splashscreen.cpp new file mode 100644 index 00000000..1f95886a --- /dev/null +++ b/src/engine/splashscreen.cpp | |||
| @@ -0,0 +1,316 @@ | |||
| 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 | using namespace Gdiplus; | ||
| 6 | |||
| 7 | #define BURN_SPLASHSCREEN_CLASS_WINDOW L"WixBurnSplashScreen" | ||
| 8 | #define IDB_SPLASHSCREEN 1 | ||
| 9 | |||
| 10 | // struct | ||
| 11 | |||
| 12 | struct SPLASHSCREEN_INFO | ||
| 13 | { | ||
| 14 | Bitmap* pBitmap; | ||
| 15 | Point pt; | ||
| 16 | Size size; | ||
| 17 | }; | ||
| 18 | |||
| 19 | struct SPLASHSCREEN_CONTEXT | ||
| 20 | { | ||
| 21 | HANDLE hIntializedEvent; | ||
| 22 | HINSTANCE hInstance; | ||
| 23 | LPCWSTR wzCaption; | ||
| 24 | |||
| 25 | HWND* pHwnd; | ||
| 26 | }; | ||
| 27 | |||
| 28 | // internal function definitions | ||
| 29 | |||
| 30 | static DWORD WINAPI ThreadProc( | ||
| 31 | __in LPVOID pvContext | ||
| 32 | ); | ||
| 33 | static LRESULT CALLBACK WndProc( | ||
| 34 | __in HWND hWnd, | ||
| 35 | __in UINT uMsg, | ||
| 36 | __in WPARAM wParam, | ||
| 37 | __in LPARAM lParam | ||
| 38 | ); | ||
| 39 | static void OnPaint( | ||
| 40 | __in HDC hdc, | ||
| 41 | __in SPLASHSCREEN_INFO* pSplashScreen | ||
| 42 | ); | ||
| 43 | static HRESULT LoadSplashScreen( | ||
| 44 | __in HMODULE hInstance, | ||
| 45 | __in SPLASHSCREEN_INFO* pSplashScreen | ||
| 46 | ); | ||
| 47 | |||
| 48 | |||
| 49 | // function definitions | ||
| 50 | |||
| 51 | extern "C" void SplashScreenCreate( | ||
| 52 | __in HINSTANCE hInstance, | ||
| 53 | __in_z_opt LPCWSTR wzCaption, | ||
| 54 | __out HWND* pHwnd | ||
| 55 | ) | ||
| 56 | { | ||
| 57 | HRESULT hr = S_OK; | ||
| 58 | SPLASHSCREEN_CONTEXT context = { }; | ||
| 59 | HANDLE rgSplashScreenEvents[2] = { }; | ||
| 60 | DWORD dwSplashScreenThreadId = 0; | ||
| 61 | |||
| 62 | rgSplashScreenEvents[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL); | ||
| 63 | ExitOnNullWithLastError(rgSplashScreenEvents[0], hr, "Failed to create modal event."); | ||
| 64 | |||
| 65 | // create splash screen thread. | ||
| 66 | context.hIntializedEvent = rgSplashScreenEvents[0]; | ||
| 67 | context.hInstance = hInstance; | ||
| 68 | context.wzCaption = wzCaption; | ||
| 69 | context.pHwnd = pHwnd; | ||
| 70 | |||
| 71 | rgSplashScreenEvents[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, &dwSplashScreenThreadId); | ||
| 72 | ExitOnNullWithLastError(rgSplashScreenEvents[1], hr, "Failed to create UI thread."); | ||
| 73 | |||
| 74 | // It doesn't really matter if the thread gets initialized (WAIT_OBJECT_0) or fails and exits | ||
| 75 | // prematurely (WAIT_OBJECT_0 + 1), we just want to wait long enough for one of those two | ||
| 76 | // events to happen. | ||
| 77 | ::WaitForMultipleObjects(countof(rgSplashScreenEvents), rgSplashScreenEvents, FALSE, INFINITE); | ||
| 78 | |||
| 79 | LExit: | ||
| 80 | ReleaseHandle(rgSplashScreenEvents[1]); | ||
| 81 | ReleaseHandle(rgSplashScreenEvents[0]); | ||
| 82 | } | ||
| 83 | |||
| 84 | extern "C" HRESULT SplashScreenDisplayError( | ||
| 85 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 86 | __in_z LPCWSTR wzBundleName, | ||
| 87 | __in HRESULT hrError | ||
| 88 | ) | ||
| 89 | { | ||
| 90 | HRESULT hr = S_OK; | ||
| 91 | LPWSTR sczDisplayString = NULL; | ||
| 92 | |||
| 93 | hr = StrAllocFromError(&sczDisplayString, hrError, NULL); | ||
| 94 | ExitOnFailure(hr, "Failed to allocate string to display error message"); | ||
| 95 | |||
| 96 | Trace(REPORT_STANDARD, "Error message displayed because: %ls", sczDisplayString); | ||
| 97 | |||
| 98 | if (BOOTSTRAPPER_DISPLAY_NONE == display || BOOTSTRAPPER_DISPLAY_PASSIVE == display || BOOTSTRAPPER_DISPLAY_EMBEDDED == display) | ||
| 99 | { | ||
| 100 | // Don't display the error dialog in these modes | ||
| 101 | ExitFunction1(hr = S_OK); | ||
| 102 | } | ||
| 103 | |||
| 104 | ::MessageBoxW(NULL, sczDisplayString, wzBundleName, MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); | ||
| 105 | |||
| 106 | LExit: | ||
| 107 | ReleaseStr(sczDisplayString); | ||
| 108 | |||
| 109 | return hr; | ||
| 110 | } | ||
| 111 | |||
| 112 | |||
| 113 | static DWORD WINAPI ThreadProc( | ||
| 114 | __in LPVOID pvContext | ||
| 115 | ) | ||
| 116 | { | ||
| 117 | HRESULT hr = S_OK; | ||
| 118 | |||
| 119 | ULONG_PTR token = 0; | ||
| 120 | GdiplusStartupInput input; | ||
| 121 | GdiplusStartupOutput output = { }; | ||
| 122 | |||
| 123 | SPLASHSCREEN_CONTEXT* pContext = static_cast<SPLASHSCREEN_CONTEXT*>(pvContext); | ||
| 124 | SPLASHSCREEN_INFO splashScreen = { }; | ||
| 125 | |||
| 126 | WNDCLASSW wc = { }; | ||
| 127 | BOOL fRegistered = TRUE; | ||
| 128 | HWND hWnd = NULL; | ||
| 129 | |||
| 130 | BOOL fRet = FALSE; | ||
| 131 | MSG msg = { }; | ||
| 132 | |||
| 133 | input.GdiplusVersion = 1; | ||
| 134 | |||
| 135 | hr = GdipInitialize(&input, &token, &output); | ||
| 136 | ExitOnFailure(hr, "Failed to initialize GDI+."); | ||
| 137 | |||
| 138 | hr = LoadSplashScreen(pContext->hInstance, &splashScreen); | ||
| 139 | ExitOnFailure(hr, "Failed to load splash screen."); | ||
| 140 | |||
| 141 | // Register the window class and create the window. | ||
| 142 | wc.lpfnWndProc = WndProc; | ||
| 143 | wc.hInstance = pContext->hInstance; | ||
| 144 | wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); | ||
| 145 | wc.lpszClassName = BURN_SPLASHSCREEN_CLASS_WINDOW; | ||
| 146 | if (!::RegisterClassW(&wc)) | ||
| 147 | { | ||
| 148 | ExitWithLastError(hr, "Failed to register window."); | ||
| 149 | } | ||
| 150 | |||
| 151 | fRegistered = TRUE; | ||
| 152 | |||
| 153 | hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, pContext->wzCaption, WS_POPUP | WS_VISIBLE, splashScreen.pt.X, splashScreen.pt.Y, splashScreen.size.Width, splashScreen.size.Height, HWND_DESKTOP, NULL, pContext->hInstance, &splashScreen); | ||
| 154 | ExitOnNullWithLastError(hWnd, hr, "Failed to create window."); | ||
| 155 | |||
| 156 | // Return the splash screen window and free the main thread waiting for us to be initialized. | ||
| 157 | *pContext->pHwnd = hWnd; | ||
| 158 | ::SetEvent(pContext->hIntializedEvent); | ||
| 159 | |||
| 160 | // Pump messages until the bootstrapper application destroys the window. | ||
| 161 | while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) | ||
| 162 | { | ||
| 163 | if (-1 == fRet) | ||
| 164 | { | ||
| 165 | hr = E_UNEXPECTED; | ||
| 166 | ExitOnFailure(hr, "Unexpected return value from message pump."); | ||
| 167 | } | ||
| 168 | else if (!::IsDialogMessageW(hWnd, &msg)) | ||
| 169 | { | ||
| 170 | ::TranslateMessage(&msg); | ||
| 171 | ::DispatchMessageW(&msg); | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | LExit: | ||
| 176 | if (fRegistered) | ||
| 177 | { | ||
| 178 | ::UnregisterClassW(BURN_SPLASHSCREEN_CLASS_WINDOW, pContext->hInstance); | ||
| 179 | } | ||
| 180 | |||
| 181 | if (splashScreen.pBitmap) | ||
| 182 | { | ||
| 183 | delete splashScreen.pBitmap; | ||
| 184 | } | ||
| 185 | |||
| 186 | if (token) | ||
| 187 | { | ||
| 188 | GdipUninitialize(token); | ||
| 189 | } | ||
| 190 | |||
| 191 | return hr; | ||
| 192 | } | ||
| 193 | |||
| 194 | static LRESULT CALLBACK WndProc( | ||
| 195 | __in HWND hWnd, | ||
| 196 | __in UINT uMsg, | ||
| 197 | __in WPARAM wParam, | ||
| 198 | __in LPARAM lParam | ||
| 199 | ) | ||
| 200 | { | ||
| 201 | LRESULT lres = 0; | ||
| 202 | SPLASHSCREEN_INFO* pSplashScreen = reinterpret_cast<SPLASHSCREEN_INFO*>(::GetWindowLongW(hWnd, GWLP_USERDATA)); | ||
| 203 | |||
| 204 | switch (uMsg) | ||
| 205 | { | ||
| 206 | case WM_NCCREATE: | ||
| 207 | { | ||
| 208 | LPCREATESTRUCTW lpcs = reinterpret_cast<LPCREATESTRUCTW>(lParam); | ||
| 209 | ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(lpcs->lpCreateParams)); | ||
| 210 | } | ||
| 211 | break; | ||
| 212 | |||
| 213 | case WM_NCDESTROY: | ||
| 214 | lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); | ||
| 215 | ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); | ||
| 216 | return lres; | ||
| 217 | |||
| 218 | case WM_NCHITTEST: | ||
| 219 | return HTCAPTION; // allow window to be moved by grabbing any pixel. | ||
| 220 | |||
| 221 | case WM_DESTROY: | ||
| 222 | ::PostQuitMessage(0); | ||
| 223 | return 0; | ||
| 224 | |||
| 225 | case WM_ERASEBKGND: | ||
| 226 | // The splash screen image will be repainted in its entirety. | ||
| 227 | return 1; | ||
| 228 | |||
| 229 | case WM_PAINT: | ||
| 230 | { | ||
| 231 | PAINTSTRUCT ps = { }; | ||
| 232 | |||
| 233 | HDC hdc = BeginPaint(hWnd, &ps); | ||
| 234 | OnPaint(hdc, pSplashScreen); | ||
| 235 | EndPaint(hWnd, &ps); | ||
| 236 | } | ||
| 237 | return 0; | ||
| 238 | } | ||
| 239 | |||
| 240 | return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); | ||
| 241 | } | ||
| 242 | |||
| 243 | static void OnPaint( | ||
| 244 | __in HDC hdc, | ||
| 245 | __in SPLASHSCREEN_INFO* pSplashScreen | ||
| 246 | ) | ||
| 247 | { | ||
| 248 | // Use high-quality bicubuc stretching from GDI+ which looks better than GDI. | ||
| 249 | Graphics graphics(hdc); | ||
| 250 | graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic); | ||
| 251 | |||
| 252 | Rect dst(0, 0, pSplashScreen->size.Width, pSplashScreen->size.Height); | ||
| 253 | Status status = graphics.DrawImage(pSplashScreen->pBitmap, dst); | ||
| 254 | |||
| 255 | #if DEBUG | ||
| 256 | HRESULT hr = GdipHresultFromStatus(status); | ||
| 257 | TraceError(hr, "Failed to draw splash screen bitmap."); | ||
| 258 | #else | ||
| 259 | UNREFERENCED_PARAMETER(status); | ||
| 260 | #endif | ||
| 261 | } | ||
| 262 | |||
| 263 | static HRESULT LoadSplashScreen( | ||
| 264 | __in HMODULE hInstance, | ||
| 265 | __in SPLASHSCREEN_INFO* pSplashScreen | ||
| 266 | ) | ||
| 267 | { | ||
| 268 | HRESULT hr = S_OK; | ||
| 269 | POINT ptCursor = { }; | ||
| 270 | HMONITOR hMonitor = NULL; | ||
| 271 | MONITORINFOEXW mi; | ||
| 272 | HDC hdc = NULL; | ||
| 273 | UINT dpiX = 0; | ||
| 274 | UINT dpiY = 0; | ||
| 275 | |||
| 276 | pSplashScreen->pBitmap = Bitmap::FromResource(hInstance, MAKEINTRESOURCEW(IDB_SPLASHSCREEN)); | ||
| 277 | ExitOnNull(pSplashScreen->pBitmap, hr, E_INVALIDDATA, "Failed to find the splash screen bitmap."); | ||
| 278 | ExitOnGdipFailure(pSplashScreen->pBitmap->GetLastStatus(), hr, "Failed to load the splash screen bitmap."); | ||
| 279 | |||
| 280 | pSplashScreen->pt.X = CW_USEDEFAULT; | ||
| 281 | pSplashScreen->pt.Y = CW_USEDEFAULT; | ||
| 282 | pSplashScreen->size.Width = pSplashScreen->pBitmap->GetWidth(); | ||
| 283 | pSplashScreen->size.Height = pSplashScreen->pBitmap->GetHeight(); | ||
| 284 | |||
| 285 | // Stretch and center the window on the monitor with the mouse. | ||
| 286 | if (::GetCursorPos(&ptCursor)) | ||
| 287 | { | ||
| 288 | hMonitor = ::MonitorFromPoint(ptCursor, MONITOR_DEFAULTTONEAREST); | ||
| 289 | if (hMonitor) | ||
| 290 | { | ||
| 291 | ZeroMemory(&mi, sizeof(mi)); | ||
| 292 | mi.cbSize = sizeof(mi); | ||
| 293 | |||
| 294 | if (::GetMonitorInfoW(hMonitor, &mi)) | ||
| 295 | { | ||
| 296 | hdc = ::CreateDCW(L"DISPLAY", mi.szDevice, NULL, NULL); | ||
| 297 | if (hdc) | ||
| 298 | { | ||
| 299 | dpiX = ::GetDeviceCaps(hdc, LOGPIXELSX); | ||
| 300 | dpiY = ::GetDeviceCaps(hdc, LOGPIXELSY); | ||
| 301 | |||
| 302 | pSplashScreen->size.Width = pSplashScreen->size.Width * dpiX / 96; | ||
| 303 | pSplashScreen->size.Height = pSplashScreen->size.Height * dpiY / 96; | ||
| 304 | |||
| 305 | ::ReleaseDC(NULL, hdc); | ||
| 306 | } | ||
| 307 | |||
| 308 | pSplashScreen->pt.X = mi.rcWork.left + (mi.rcWork.right - mi.rcWork.left - pSplashScreen->size.Width) / 2; | ||
| 309 | pSplashScreen->pt.Y = mi.rcWork.top + (mi.rcWork.bottom - mi.rcWork.top - pSplashScreen->size.Height) / 2; | ||
| 310 | } | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | LExit: | ||
| 315 | return hr; | ||
| 316 | } | ||
