所有資源載入 API (LoadBitmap, LoadIcon, LoadMenu, LoadString, DialogBoxParam, CreateDialogParam) 的**第一個參數都是 HINSTANCE hInstance** —— 包含資源的模組代碼。
invoke GetModuleHandle, NULL 取得當前 EXE/DLL 代碼。invoke LoadLibrary, "MyRes.dll" 返回值即為該 DLL 的 HINSTANCE。一行代碼切換語言包:主程式邏輯不變,LoadLibrary("LangCHT.dll") vs LoadLibrary("LangENU.dll"),後續所有 LoadString/DialogBoxParam 傳入對應代碼即可。
| API | 用途 | 關鍵參數 |
|---|---|---|
LoadBitmap(hInst, lpBitmapName) | 載入位圖資源 | 返回 HBITMAP |
LoadIcon(hInst, lpIconName) | 載入圖示資源 | 返回 HICON |
LoadMenu(hInst, lpMenuName) | 載入選單資源 | 返回 HMENU |
LoadString(hInst, uID, lpBuffer, cchBufferMax) | 載入字串表項目 | 返回複製字元數 |
DialogBoxParam(hInst, lpTemplate, hWndParent, lpDialogFunc, dwInitParam) | 建立**模態**對話框 | 需對話框模板資源 ID、回呼程序 |
CreateDialogParam(hInst, lpTemplate, hWndParent, lpDialogFunc, dwInitParam) | 建立**非模態**對話框 | 返回 HWND,需訊息迴圈 IsDialogMessage |
invoke LoadLibrary, DLL_FILENAME
mov DLL_HANDLE, eax
invoke LoadBitmap, DLL_HANDLE, BITMAP_ID
invoke LoadIcon, DLL_HANDLE, ICON_ID
invoke LoadMenu, DLL_HANDLE, MENU_ID
invoke LoadString, DLL_HANDLE, STRING_ID, addr StrBuffer, sizeof StrBuffer
invoke DialogBoxParam, DLL_HANDLE, DLG_NAME, hParent, DlgProc, lParam
; EXE 代碼
DlgProc proc hWnd, uMsg, wParam, lParam
; 處理對話框訊息...
ret
DlgProc endp
; 呼叫 DLL 對話框
invoke DialogBoxParam, DLL_HANDLE, DLG_ID, hWnd, offset DlgProc, 0
問題:若 DLL 是**外掛/擴充模組**,主程式不知其內部邏輯;DLL 更新時主程式需重新編譯;多 DLL 時主程式膨脹。
原則:純資源 DLL (只換文字/圖片) 可共用主程式回呼;**功能性 DLL/外掛,回呼必須內建於 DLL**。
; --- 導出函式:LoadDialog ---
LoadDialog proc hInstance:DWORD, hWnd:DWORD, ID:DWORD
.if ID == 100
mov eax, offset DlgProc0
.elseif ID == 101
mov eax, offset DlgProc1
.elseif ID == 102
mov eax, offset DlgProc2
.elseif ID == 103
mov eax, offset DlgProc3
.endif
invoke DialogBoxParam, hInstance, ID, hWnd, eax, NULL
ret
LoadDialog endp
; --- 內建回呼程序 ---
DlgProc0 proc ... ; 對話框 100 邏輯 ... ret; endp
DlgProc1 proc ... ; 對話框 101 邏輯 ... ret; endp
DlgProc2 proc ... ; 對話框 102 邏輯 ... ret; endp
DlgProc3 proc ... ; 對話框 103 邏輯 ... ret; endp
EXPORTS
LoadDialog
; 初始化:只需取得一次 LoadDialog 位址
invoke GetProcAddress, DLL_HANDLE, "LoadDialog"
mov pLoadDialog, eax
; 任意位置需要彈出對話框 101:
push 101
push hWnd
push DLL_HANDLE
call [pLoadDialog]
; 需要對話框 102:
push 102
push hWnd
push DLL_HANDLE
call [pLoadDialog]
| 方案 | 回呼位置 | 導出需求 | 主程式複雜度 | 適用場景 |
|---|---|---|---|---|
| 傳統 | EXE | 無 (直接呼叫) | 高 (每個對話框需知回呼位址) | 純資源切換 (多語言) |
| GetProcAddress 每次查 | DLL | 每個 DlgProc 皆需導出 | 中 (重複呼叫 GetProcAddress) | 少量對話框 |
| 封裝 LoadDialog (推薦) | DLL | 僅導出 LoadDialog | 低 (統一入口,ID 驅動) | 外掛、多對話框、功能性 DLL |
LoadBitmap/LoadIcon/LoadMenu 返回的代碼,**程式結束前必須呼叫對應 DeleteObject/DestroyIcon/DestroyMenu 釋放**,否則 GDI/USER 物件洩漏。DialogBoxParam 模態對話框結束時自動銷毀內部資源。CreateDialogParam 非模態對話框需自行 DestroyWindow。LoadLibrary 增加參考計數,配對 FreeLibrary 釋放 DLL (資源隨之卸載)。invoke LoadLibrary, "Plugin.dll"
.if eax == NULL
invoke MessageBox, hWnd, "載入外掛失敗!", "錯誤", MB_ICONERROR
jmp ExitApp
.endif
mov hPlugin, eax
; ... 後續使用 ...
invoke FreeLibrary, hPlugin