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