This is Part II of a series on creating a splash screen application in native code. For more information, see the Introduction and the Code License.
In Part I I showed how to create a HBITMAP with the splash screen image. This installment will show how to create a window that displays that image.
Each Win32 window needs a window class, and the splash screen window is no different. The window class for the splash screen window will be fairly standard, although the interesting thing is that we can use DefWindowProc as the WndProc because we don’t actually need special processing for any window messages. The window class also specifies an icon because I prefer to have splash screens show up in the Alt+Tab list, and it looks better if we provide an icon. (Note that in this code, error handling has been omitted.)
// Window Class name
const TCHAR * c_szSplashClass = _T("SplashWindow");
// Registers a window class for the splash and splash owner windows.
void RegisterWindowClass()
{
WNDCLASS wc = { 0 };
wc.lpfnWndProc = DefWindowProc;
wc.hInstance = g_hInstance;
wc.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_SPLASHICON));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = c_szSplashClass;
RegisterClass(&wc);
}
When creating the windows, I use an old trick (a hidden owner window) to make
the splash screen appear in the Alt+Tab list, but not in the taskbar. If you
wanted the splash screen to also appear in the taskbar, you could drop the
hidden owner window. If you didn’t want it to appear in the Alt+Tab list or
the taskbar, you could use the WS_EX_TOOLWINDOW
extended window style (and
omit the owner window).
// Creates the splash owner window and the splash window.
HWND CreateSplashWindow()
{
HWND hwndOwner = CreateWindow(c_szSplashClass, NULL, WS_POPUP,
0, 0, 0, 0, NULL, NULL, g_hInstance, NULL);
return CreateWindowEx(WS_EX_LAYERED, c_szSplashClass, NULL, WS_POPUP | WS_VISIBLE,
0, 0, 0, 0, hwndOwner, NULL, g_hInstance, NULL);
}
The window now exists, but isn’t sized or positioned correctly, and has no content. The UpdateLayeredWindow function can be used to correct all these problems at once. There are several steps to this process:
// Calls UpdateLayeredWindow to set a bitmap (with alpha) as the content of the splash window.
void SetSplashImage(HWND hwndSplash, HBITMAP hbmpSplash)
{
// get the size of the bitmap
BITMAP bm;
GetObject(hbmpSplash, sizeof(bm), &bm);
SIZE sizeSplash = { bm.bmWidth, bm.bmHeight };
// get the primary monitor's info
POINT ptZero = { 0 };
HMONITOR hmonPrimary = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
MONITORINFO monitorinfo = { 0 };
monitorinfo.cbSize = sizeof(monitorinfo);
GetMonitorInfo(hmonPrimary, &monitorinfo);
// center the splash screen in the middle of the primary work area
const RECT & rcWork = monitorinfo.rcWork;
POINT ptOrigin;
ptOrigin.x = rcWork.left + (rcWork.right - rcWork.left - sizeSplash.cx) / 2;
ptOrigin.y = rcWork.top + (rcWork.bottom - rcWork.top - sizeSplash.cy) / 2;
// create a memory DC holding the splash bitmap
HDC hdcScreen = GetDC(NULL);
HDC hdcMem = CreateCompatibleDC(hdcScreen);
HBITMAP hbmpOld = (HBITMAP) SelectObject(hdcMem, hbmpSplash);
// use the source image's alpha channel for blending
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
// paint the window (in the right location) with the alpha-blended bitmap
UpdateLayeredWindow(hwndSplash, hdcScreen, &ptOrigin, &sizeSplash,
hdcMem, &ptZero, RGB(0, 0, 0), &blend, ULW_ALPHA);
// delete temporary objects
SelectObject(hdcMem, hbmpOld);
DeleteDC(hdcMem);
ReleaseDC(NULL, hdcScreen);
}
The beauty of layered windows and the UpdateLayeredWindow function is that the splash window doesn’t have to respond to WM_PAINT messages; Windows will paint it (and blend it correctly with the windows below it) by default.
Now that the splash screen is being displayed, we need to launch the actual application (and then dismiss the splash screen); the next installments will cover this.
Posted by Bradley Grainger on September 25, 2008