From 61847dddd4fd497057c780658e383c4627de19ec Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 29 Dec 2018 22:12:08 -0600 Subject: Import code from old v4 repo --- src/engine/splashscreen.cpp | 316 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 src/engine/splashscreen.cpp (limited to 'src/engine/splashscreen.cpp') 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 @@ +// 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. + +#include "precomp.h" + +using namespace Gdiplus; + +#define BURN_SPLASHSCREEN_CLASS_WINDOW L"WixBurnSplashScreen" +#define IDB_SPLASHSCREEN 1 + +// struct + +struct SPLASHSCREEN_INFO +{ + Bitmap* pBitmap; + Point pt; + Size size; +}; + +struct SPLASHSCREEN_CONTEXT +{ + HANDLE hIntializedEvent; + HINSTANCE hInstance; + LPCWSTR wzCaption; + + HWND* pHwnd; +}; + +// internal function definitions + +static DWORD WINAPI ThreadProc( + __in LPVOID pvContext + ); +static LRESULT CALLBACK WndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); +static void OnPaint( + __in HDC hdc, + __in SPLASHSCREEN_INFO* pSplashScreen + ); +static HRESULT LoadSplashScreen( + __in HMODULE hInstance, + __in SPLASHSCREEN_INFO* pSplashScreen + ); + + +// function definitions + +extern "C" void SplashScreenCreate( + __in HINSTANCE hInstance, + __in_z_opt LPCWSTR wzCaption, + __out HWND* pHwnd + ) +{ + HRESULT hr = S_OK; + SPLASHSCREEN_CONTEXT context = { }; + HANDLE rgSplashScreenEvents[2] = { }; + DWORD dwSplashScreenThreadId = 0; + + rgSplashScreenEvents[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL); + ExitOnNullWithLastError(rgSplashScreenEvents[0], hr, "Failed to create modal event."); + + // create splash screen thread. + context.hIntializedEvent = rgSplashScreenEvents[0]; + context.hInstance = hInstance; + context.wzCaption = wzCaption; + context.pHwnd = pHwnd; + + rgSplashScreenEvents[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, &dwSplashScreenThreadId); + ExitOnNullWithLastError(rgSplashScreenEvents[1], hr, "Failed to create UI thread."); + + // It doesn't really matter if the thread gets initialized (WAIT_OBJECT_0) or fails and exits + // prematurely (WAIT_OBJECT_0 + 1), we just want to wait long enough for one of those two + // events to happen. + ::WaitForMultipleObjects(countof(rgSplashScreenEvents), rgSplashScreenEvents, FALSE, INFINITE); + +LExit: + ReleaseHandle(rgSplashScreenEvents[1]); + ReleaseHandle(rgSplashScreenEvents[0]); +} + +extern "C" HRESULT SplashScreenDisplayError( + __in BOOTSTRAPPER_DISPLAY display, + __in_z LPCWSTR wzBundleName, + __in HRESULT hrError + ) +{ + HRESULT hr = S_OK; + LPWSTR sczDisplayString = NULL; + + hr = StrAllocFromError(&sczDisplayString, hrError, NULL); + ExitOnFailure(hr, "Failed to allocate string to display error message"); + + Trace(REPORT_STANDARD, "Error message displayed because: %ls", sczDisplayString); + + if (BOOTSTRAPPER_DISPLAY_NONE == display || BOOTSTRAPPER_DISPLAY_PASSIVE == display || BOOTSTRAPPER_DISPLAY_EMBEDDED == display) + { + // Don't display the error dialog in these modes + ExitFunction1(hr = S_OK); + } + + ::MessageBoxW(NULL, sczDisplayString, wzBundleName, MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); + +LExit: + ReleaseStr(sczDisplayString); + + return hr; +} + + +static DWORD WINAPI ThreadProc( + __in LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + + ULONG_PTR token = 0; + GdiplusStartupInput input; + GdiplusStartupOutput output = { }; + + SPLASHSCREEN_CONTEXT* pContext = static_cast(pvContext); + SPLASHSCREEN_INFO splashScreen = { }; + + WNDCLASSW wc = { }; + BOOL fRegistered = TRUE; + HWND hWnd = NULL; + + BOOL fRet = FALSE; + MSG msg = { }; + + input.GdiplusVersion = 1; + + hr = GdipInitialize(&input, &token, &output); + ExitOnFailure(hr, "Failed to initialize GDI+."); + + hr = LoadSplashScreen(pContext->hInstance, &splashScreen); + ExitOnFailure(hr, "Failed to load splash screen."); + + // Register the window class and create the window. + wc.lpfnWndProc = WndProc; + wc.hInstance = pContext->hInstance; + wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); + wc.lpszClassName = BURN_SPLASHSCREEN_CLASS_WINDOW; + if (!::RegisterClassW(&wc)) + { + ExitWithLastError(hr, "Failed to register window."); + } + + fRegistered = TRUE; + + 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); + ExitOnNullWithLastError(hWnd, hr, "Failed to create window."); + + // Return the splash screen window and free the main thread waiting for us to be initialized. + *pContext->pHwnd = hWnd; + ::SetEvent(pContext->hIntializedEvent); + + // Pump messages until the bootstrapper application destroys the window. + while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) + { + if (-1 == fRet) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected return value from message pump."); + } + else if (!::IsDialogMessageW(hWnd, &msg)) + { + ::TranslateMessage(&msg); + ::DispatchMessageW(&msg); + } + } + +LExit: + if (fRegistered) + { + ::UnregisterClassW(BURN_SPLASHSCREEN_CLASS_WINDOW, pContext->hInstance); + } + + if (splashScreen.pBitmap) + { + delete splashScreen.pBitmap; + } + + if (token) + { + GdipUninitialize(token); + } + + return hr; +} + +static LRESULT CALLBACK WndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + LRESULT lres = 0; + SPLASHSCREEN_INFO* pSplashScreen = reinterpret_cast(::GetWindowLongW(hWnd, GWLP_USERDATA)); + + switch (uMsg) + { + case WM_NCCREATE: + { + LPCREATESTRUCTW lpcs = reinterpret_cast(lParam); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(lpcs->lpCreateParams)); + } + break; + + case WM_NCDESTROY: + lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); + return lres; + + case WM_NCHITTEST: + return HTCAPTION; // allow window to be moved by grabbing any pixel. + + case WM_DESTROY: + ::PostQuitMessage(0); + return 0; + + case WM_ERASEBKGND: + // The splash screen image will be repainted in its entirety. + return 1; + + case WM_PAINT: + { + PAINTSTRUCT ps = { }; + + HDC hdc = BeginPaint(hWnd, &ps); + OnPaint(hdc, pSplashScreen); + EndPaint(hWnd, &ps); + } + return 0; + } + + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +static void OnPaint( + __in HDC hdc, + __in SPLASHSCREEN_INFO* pSplashScreen + ) +{ + // Use high-quality bicubuc stretching from GDI+ which looks better than GDI. + Graphics graphics(hdc); + graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic); + + Rect dst(0, 0, pSplashScreen->size.Width, pSplashScreen->size.Height); + Status status = graphics.DrawImage(pSplashScreen->pBitmap, dst); + +#if DEBUG + HRESULT hr = GdipHresultFromStatus(status); + TraceError(hr, "Failed to draw splash screen bitmap."); +#else + UNREFERENCED_PARAMETER(status); +#endif +} + +static HRESULT LoadSplashScreen( + __in HMODULE hInstance, + __in SPLASHSCREEN_INFO* pSplashScreen + ) +{ + HRESULT hr = S_OK; + POINT ptCursor = { }; + HMONITOR hMonitor = NULL; + MONITORINFOEXW mi; + HDC hdc = NULL; + UINT dpiX = 0; + UINT dpiY = 0; + + pSplashScreen->pBitmap = Bitmap::FromResource(hInstance, MAKEINTRESOURCEW(IDB_SPLASHSCREEN)); + ExitOnNull(pSplashScreen->pBitmap, hr, E_INVALIDDATA, "Failed to find the splash screen bitmap."); + ExitOnGdipFailure(pSplashScreen->pBitmap->GetLastStatus(), hr, "Failed to load the splash screen bitmap."); + + pSplashScreen->pt.X = CW_USEDEFAULT; + pSplashScreen->pt.Y = CW_USEDEFAULT; + pSplashScreen->size.Width = pSplashScreen->pBitmap->GetWidth(); + pSplashScreen->size.Height = pSplashScreen->pBitmap->GetHeight(); + + // Stretch and center the window on the monitor with the mouse. + if (::GetCursorPos(&ptCursor)) + { + hMonitor = ::MonitorFromPoint(ptCursor, MONITOR_DEFAULTTONEAREST); + if (hMonitor) + { + ZeroMemory(&mi, sizeof(mi)); + mi.cbSize = sizeof(mi); + + if (::GetMonitorInfoW(hMonitor, &mi)) + { + hdc = ::CreateDCW(L"DISPLAY", mi.szDevice, NULL, NULL); + if (hdc) + { + dpiX = ::GetDeviceCaps(hdc, LOGPIXELSX); + dpiY = ::GetDeviceCaps(hdc, LOGPIXELSY); + + pSplashScreen->size.Width = pSplashScreen->size.Width * dpiX / 96; + pSplashScreen->size.Height = pSplashScreen->size.Height * dpiY / 96; + + ::ReleaseDC(NULL, hdc); + } + + pSplashScreen->pt.X = mi.rcWork.left + (mi.rcWork.right - mi.rcWork.left - pSplashScreen->size.Width) / 2; + pSplashScreen->pt.Y = mi.rcWork.top + (mi.rcWork.bottom - mi.rcWork.top - pSplashScreen->size.Height) / 2; + } + } + } + +LExit: + return hr; +} -- cgit v1.2.3-55-g6feb