首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 組合語言

Win32 組合語言實作啟動畫面:資源 DLL 動態載入與定時器控制

     編輯:組合語言

前言

專業軟體啟動時常顯示一個無標題列、無邊框的啟動畫面,展示品牌、版本資訊,同時背景進行初始化。本文示範如何用 資源 DLL + 定時器 + WS_POPUP 視窗 實作啟動畫面,主程式載入 DLL 顯示畫面,DLL 內部自行管理視窗生命週期,時間到或使用者點擊即自動關閉並卸載 DLL,不佔用主程式記憶體。

架構設計

  • 主程式:LoadLibrary("splash.dll") → 等待 DLL 初始化完成 → FreeLibrary 卸載 → 繼續主流程。
  • DLL (splash.dll):
    1. DllEntry 收到 DLL_PROCESS_ATTACH 呼叫 ShowBitMap。
    2. 註冊/建立 WS_POPUP 視窗(無標題、無邊框),固定大小 250x250。
    3. 載入資源位圖,建立定時器(2 秒)。
    4. 訊息迴路處理 WM_PAINT 繪製位圖(用 StretchBlt 填滿視窗)、WM_TIMER 觸發關閉、WM_LBUTTONDOWN 使用者點擊關閉。
    5. 視窗銷毀時清理資源,PostQuitMessage 結束 DLL 內部訊息迴路,控制權返回主程式 LoadLibrary 之後。

關鍵 API

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

DLL (splash.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
        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

關鍵技術細節解析

  1. 資源隔離:位圖放在 DLL 資源區,主程式僅在需要時 LoadLibrary,用畢 FreeLibrary,記憶體佔用極小。
  2. DLL 入口點同步:LoadLibrary 會阻塞直到 DllEntry 返回。本例在 DllEntry 直接跑完整訊息迴路,因此主程式會在啟動畫面關閉後才繼續執行。
  3. WS_POPUP 視窗:無 WS_CAPTION、WS_SYSMENU、WS_THICKFRAME,純內容區域。
  4. 螢幕置中算法:取得桌面與視窗矩形,計算 (DesktopWidth - DlgWidth)/2, (DesktopHeight - DlgHeight)/2 傳給 MoveWindow。
  5. StretchBlt 適應性縮放:位圖原始尺寸任意,GetObject 取得 bmWidth/bmHeight,繪製時拉伸至 250x250 視窗。
  6. 使用者可提前關閉:處理 WM_LBUTTONDOWN 呼叫 DestroyWindow,定時器到時發送同樣訊息復用關閉邏輯。

改進方向

  • 非同步顯示:主程式建立執行緒載入 DLL,同時進行初始化,避免阻塞。
  • 淡入/淡出動畫:使用 AlphaBlend 或分層視窗 (WS_EX_LAYERED) 實現漸變效果。
  • 進度條/狀態文字:在 WM_PAINT 繪製動態資訊,主程式透過共享記憶體或訊息與 DLL 通訊。
  • 高 DPI 支援:啟用 SetProcessDPIAware,位圖提供多解析度版本。

總結

啟動畫面的經典實作模式:資源 DLL + WS_POPUP 視窗 + 定時器/使用者互動關閉。此架構模組化程度高、主程式乾淨、資源按需載入,是 Win32 組合語言開發專業級應用程式的標準技巧之一。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved