Tutorial 25: Simple Bitmap | Tutorial 26: Splash Screen | Tutorial 27: Tooltip Control |
Now that we know how to use a bitmap, we can progress to a more creative use of it. Splash screen.
Download the example.
A splash screen is a window that has no title bar, no system menu box, no border that displays a bitmap for a while and then disappears automatically. It's usually used during program startup, to display the program's logo or to distract the user's attention while the program does some lengthy initialization. We will implement a splash screen in this tutorial.
The first step is to include the bitmap in the resource file. However, if you think of it, it's a waste of precious memory to load a bitmap that will be used only once and keep it in memory till the program is closed. A better solution is to create a *resource* DLL which contains the bitmap and has the sole purpose of displaying the splash screen. This way, you can load the DLL when you want to display the splash screen and unload it when it's not necessary anymore. So we will have two modules: the main program and the splash DLL. We will put the bitmap into the DLL's resource.
We will examine the mechanisms in detail.
You can dynamically load a DLL with LoadLibrary function which
has the following syntax:
LoadLibrary PROTO lpDLLName:DWORD
It takes only one parameter:
the address of the name of the DLL you want to load into memory.
If the call is successful, it returns the module handle of the
DLL else it returns NULL.
To unload a DLL, call FreeLibrary:
FreeLibrary PROTO hLib:DWORD
It takes one parameter:
the module handle of the DLL you want to unload.
Normally, you got that handle from LoadLibrary
SetTimer PROTO hWnd:DWORD, TimerID:DWORD, uElapse:DWORD, \
lpTimerFunc:DWORD
SetTimer returns the ID of the timer if successful. Otherwise it returns NULL. So it's best not to use the timer ID of 0.
KillTimer PROTO hWnd:DWORD, TimerID:DWORD
;-----------------------------------------------------------------------
; The main program
;-----------------------------------------------------------------------
.386
.model FLAT,STDCALL
OPTION casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
.data
ClassName db "SplashDemoWinClass",0
AppName db "Splash Screen Example",0
Libname db "splash.dll",0
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.code
start:
invoke LoadLibrary,ADDR Libname
.IF eax!=NULL
invoke FreeLibrary,eax
.ENDIF
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain PROC hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,
CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, ADDR wc
invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW, \
CW_USEDEFAULT,CW_USEDEFAULT, \
CW_USEDEFAULT,CW_USEDEFAULT, \
NULL,NULL,hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain ENDP
WndProc PROC hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc ENDP
END start
;--------------------------------------------------------------------
The Bitmap DLL
;--------------------------------------------------------------------
.386
.model FLAT,STDCALL
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib
.data
BitmapName db "MySplashBMP",0
ClassName db "SplashWndClass",0
hBitMap dd 0
TimerID dd 0
.data
hInstance dd ?
.code
DllEntry PROC hInst:DWORD, reason:DWORD, reserved1:DWORD
.IF reason==DLL_PROCESS_ATTACH ; When the dll is loaded
push hInst
pop hInstance
call ShowBitMap
.ENDIF
mov eax,TRUE
ret
DllEntry Endp
ShowBitMap PROC
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,0
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, ADDR wc
invoke CreateWindowEx,NULL,ADDR ClassName,NULL, \
WS_POPUP, \
CW_USEDEFAULT,CW_USEDEFAULT,\
250,250, \
NULL,NULL,hInstance,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
ShowBitMap ENDP
WndProc PROC hWnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
LOCAL ps:PAINTSTRUCT
LOCAL hdc:HDC
LOCAL hMemoryDC:HDC
LOCAL hOldBmp:DWORD
LOCAL bitmap:BITMAP
LOCAL DlgHeight:DWORD
LOCAL DlgWidth:DWORD
LOCAL DlgRect:RECT
LOCAL DesktopRect:RECT
.IF uMsg==WM_DESTROY
.IF hBitMap!=0
invoke DeleteObject,hBitMap
.ENDIF
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_CREATE
invoke GetWindowRect,hWnd,ADDR DlgRect
invoke GetDesktopWindow
mov ecx,eax
invoke GetWindowRect,ecx,ADDR DesktopRect
push 0
mov eax,DlgRect.bottom
sub eax,DlgRect.top
mov DlgHeight,eax
push eax
mov eax,DlgRect.right
sub eax,DlgRect.left
mov DlgWidth,eax
push eax
mov eax,DesktopRect.bottom
sub eax,DlgHeight
shr eax,1
push eax
mov eax,DesktopRect.right
sub eax,DlgWidth
shr eax,1
push eax
push hWnd
call MoveWindow
invoke LoadBitmap,hInstance,ADDR BitmapName
mov hBitMap,eax
invoke SetTimer,hWnd,1,2000,NULL
mov TimerID,eax
.ELSEIF uMsg==WM_TIMER
invoke SendMessage,hWnd,WM_LBUTTONDOWN,NULL,NULL
invoke KillTimer,hWnd,TimerID
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint,hWnd,ADDR ps
mov hdc,eax
invoke CreateCompatibleDC,hdc
mov hMemoryDC,eax
invoke SelectObject,eax,hBitMap
mov hOldBmp,eax
invoke GetObject,hBitMap,sizeof BITMAP,ADDR bitmap
invoke StretchBlt,hdc,0,0,250,250,hMemoryDC,0,0, \
bitmap.bmWidth,bitmap.bmHeight,SRCCOPY
invoke SelectObject,hMemoryDC,hOldBmp
invoke DeleteDC,hMemoryDC
invoke EndPaint,hWnd,ADDR ps
.ELSEIF uMsg==WM_LBUTTONDOWN
invoke DestroyWindow,hWnd
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc ENDP
END DllEntry
We will examine the code in the main program first.
invoke LoadLibrary,ADDR Libname
IF eax!=NULL
invoke FreeLibrary,eax
.ENDIF
We call LoadLibrary to load the DLL named "splash.dll".
And after that, unload it from memory with FreeLibrary.
LoadLibrary will not return until the DLL is finished with
its initialization.
That's all the main program does. The interesting part is in the
DLL.
.IF reason==DLL_PROCESS_ATTACH ; When the dll is loaded
push hInst
pop hInstance
call ShowBitMap
When the DLL is loaded, Windows calls its entrypoint function
with DLL_PROCESS_ATTACH flag. We take this opportunity to
display the splash screen. First we store the instance handle
of the DLL for future use. Then call a function named
ShowBitMap to do the real job. ShowBitMap registers a
window class, creates a window and enters the message loop as
usual. The interesting part is in the CreateWindowEx call:
invoke CreateWindowEx,NULL,ADDR ClassName,NULL, \
WS_POPUP, \
CW_USEDEFAULT,CW_USEDEFAULT, \
250,250, \
NULL,NULL,hInstance,NULL
Note that the window style is only WS_POPUP which will make the window borderless and without caption. We also limit the width and height of the window to 250x250 pixels.
invoke GetWindowRect,hWnd,ADDR DlgRect
invoke GetDesktopWindow
mov ecx,eax
invoke GetWindowRect,ecx,ADDR DesktopRect
push 0
mov eax,DlgRect.bottom
sub eax,DlgRect.top
mov DlgHeight,eax
push eax
mov eax,DlgRect.right
sub eax,DlgRect.left
mov DlgWidth,eax
push eax
mov eax,DesktopRect.bottom
sub eax,DlgHeight
shr eax,1
push eax
mov eax,DesktopRect.right
sub eax,DlgWidth
shr eax,1
push eax
push hWnd
call MoveWindow
It retrieves the dimensions of the desktop and the window then calculates the appropriate coordinate of the left upper corner of the window to make it center.
invoke LoadBitmap,hInstance,ADDR BitmapName
mov hBitMap,eax
invoke SetTimer,hWnd,1,2000,NULL
mov TimerID,eax
Next it loads the bitmap from the resource with LoadBitmap and creates a timer with the timer ID of 1 and the time interval 2 seconds. The timer will send WM_TIMER messages to the window every 2 seconds.
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint,hWnd,ADDR ps
mov hdc,eax
invoke CreateCompatibleDC,hdc
mov hMemoryDC,eax
invoke SelectObject,eax,hBitMap
mov hOldBmp,eax
invoke GetObject,hBitMap,sizeof BITMAP,ADDR bitmap
invoke StretchBlt,hdc,0,0,250,250, \
hMemoryDC,0,0, \
bitmap.bmWidth,bitmap.bmHeight, \
SRCCOPY
invoke SelectObject,hMemoryDC,hOldBmp
invoke DeleteDC,hMemoryDC
invoke EndPaint,hWnd,ADDR ps
When the window receives WM_PAINT message, it creates a memory DC,
select the bitmap into the memory DC, obtain the size of the bitmap
with GetObject and then put the bitmap on the window by calling
StretchBlt which performs like BitBlt but it can stretch or compress
the bitmap to the desired dimension. In this case, we want the
bitmap to fit into the window so we use StretchBlt instead of BitBlt.
We delete the memory DC after that.
.ELSEIF uMsg==WM_LBUTTONDOWN
invoke DestroyWindow,hWnd
It would be frustrating to the user if he has to wait until the
splash screen to disappear. We can provide the user with a choice.
When he clicks on the splash screen, it will disappear.
That's why we need to process WM_LBUTTONDOWN message in the DLL.
Upon receiving this message, the window is destroyed by
DestroyWindow call.
.ELSEIF uMsg==WM_TIMER
invoke SendMessage,hWnd,WM_LBUTTONDOWN,NULL,NULL
invoke KillTimer,hWnd,TimerID
If the user chooses to wait, the splash screen will disappear when
the specified time has elapsed (in our example, it's 2 seconds).
We can do this by processing WM_TIMER message. Upon receiving this
message, we closes the window by sending WM_LBUTTONDOWN message to
the window. This is to avoid code duplication.
We don't have further use for the timer so we destroy it
with KillTimer.
When the window is closed, the DLL will return control to the main program.
Tutorial 25: Simple Bitmap | Overview | Tutorial 27: Tooltip Control |