專業軟體啟動時常顯示一個無標題列、無邊框的啟動畫面,展示品牌、版本資訊,同時背景進行初始化。本文示範如何用 資源 DLL + 定時器 + WS_POPUP 視窗 實作啟動畫面,主程式載入 DLL 顯示畫面,DLL 內部自行管理視窗生命週期,時間到或使用者點擊即自動關閉並卸載 DLL,不佔用主程式記憶體。
LoadLibrary("splash.dll") → 等待 DLL 初始化完成 → FreeLibrary 卸載 → 繼續主流程。DllEntry 收到 DLL_PROCESS_ATTACH 呼叫 ShowBitMap。WS_POPUP 視窗(無標題、無邊框),固定大小 250x250。WM_PAINT 繪製位圖(用 StretchBlt 填滿視窗)、WM_TIMER 觸發關閉、WM_LBUTTONDOWN 使用者點擊關閉。PostQuitMessage 結束 DLL 內部訊息迴路,控制權返回主程式 LoadLibrary 之後。| API | 用途 |
|---|---|
LoadLibrary / FreeLibrary | 動態載入/卸載 DLL |
SetTimer / KillTimer | 建立/銷毀定時器,WM_TIMER 通知視窗 |
CreateWindowEx(WS_POPUP, ...) | 建立無標題無邊框視窗 |
GetWindowRect / GetDesktopWindow / MoveWindow | 計算螢幕中央座標並移動視窗 |
StretchBlt | 將位圖拉伸填滿客戶區 |
.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 ; DLL 內部訊息迴路結束後返回這裡
.endif
invoke GetModuleHandle, NULL
mov hInstance, eax
invoke GetCommandLine
mov CommandLine, eax
invoke WinMain, hInstance, NULL, CommandLine, SW_SHOWDEFAULT
invoke ExitProcess, eax
; ... 標準 WinMain/WndProc 建立主視窗 ...
end start
.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
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
; 建立 WS_POPUP 視窗,無標題無邊框,固定 250x250
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
; 載入位圖 & 建立定時器 (2 秒)
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
LoadLibrary,用畢 FreeLibrary,記憶體佔用極小。LoadLibrary 會阻塞直到 DllEntry 返回。本例在 DllEntry 直接跑完整訊息迴路,因此主程式會在啟動畫面關閉後才繼續執行。WS_CAPTION、WS_SYSMENU、WS_THICKFRAME,純內容區域。(DesktopWidth - DlgWidth)/2, (DesktopHeight - DlgHeight)/2 傳給 MoveWindow。GetObject 取得 bmWidth/bmHeight,繪製時拉伸至 250x250 視窗。WM_LBUTTONDOWN 呼叫 DestroyWindow,定時器到時發送同樣訊息復用關閉邏輯。AlphaBlend 或分層視窗 (WS_EX_LAYERED) 實現漸變效果。WM_PAINT 繪製動態資訊,主程式透過共享記憶體或訊息與 DLL 通訊。SetProcessDPIAware,位圖提供多解析度版本。啟動畫面的經典實作模式:資源 DLL + WS_POPUP 視窗 + 定時器/使用者互動關閉。此架構模組化程度高、主程式乾淨、資源按需載入,是 Win32 組合語言開發專業級應用程式的標準技巧之一。