aboutsummaryrefslogtreecommitdiff
path: root/src/engine/splashscreen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/splashscreen.cpp')
-rw-r--r--src/engine/splashscreen.cpp316
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
5using namespace Gdiplus;
6
7#define BURN_SPLASHSCREEN_CLASS_WINDOW L"WixBurnSplashScreen"
8#define IDB_SPLASHSCREEN 1
9
10// struct
11
12struct SPLASHSCREEN_INFO
13{
14 Bitmap* pBitmap;
15 Point pt;
16 Size size;
17};
18
19struct SPLASHSCREEN_CONTEXT
20{
21 HANDLE hIntializedEvent;
22 HINSTANCE hInstance;
23 LPCWSTR wzCaption;
24
25 HWND* pHwnd;
26};
27
28// internal function definitions
29
30static DWORD WINAPI ThreadProc(
31 __in LPVOID pvContext
32 );
33static LRESULT CALLBACK WndProc(
34 __in HWND hWnd,
35 __in UINT uMsg,
36 __in WPARAM wParam,
37 __in LPARAM lParam
38 );
39static void OnPaint(
40 __in HDC hdc,
41 __in SPLASHSCREEN_INFO* pSplashScreen
42 );
43static HRESULT LoadSplashScreen(
44 __in HMODULE hInstance,
45 __in SPLASHSCREEN_INFO* pSplashScreen
46 );
47
48
49// function definitions
50
51extern "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
79LExit:
80 ReleaseHandle(rgSplashScreenEvents[1]);
81 ReleaseHandle(rgSplashScreenEvents[0]);
82}
83
84extern "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
106LExit:
107 ReleaseStr(sczDisplayString);
108
109 return hr;
110}
111
112
113static 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
175LExit:
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
194static 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
243static 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
263static 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
314LExit:
315 return hr;
316}