在 Windows 程式開發中,我們每天都在呼叫系統 DLL(kernel32.dll, user32.dll)裡的函式。但若能將常用演算法、專有邏輯封裝成自訂 DLL,不僅能實現程式碼重複使用、減小主程式體積,還能支援多語言模組化、熱插拔擴充等進階架構。本文將手把手教您使用 MASM32 從零建立一個 DLL,導出 OutEdxEax 函式(將 64 位整數 EDX:EAX 轉為十進位字串),並撰寫測試程式呼叫驗證。
一個標準的 MASM32 DLL 專案通常包含:
DllEntry 與導出函式。includelib 與 proto 使用。; MyDll.asm - 動態連結庫原始碼
; 編譯模式="DLL" (MASM for EditPlus 專用指示)
.386
.model flat, stdcall
option casemap:none
include windows.inc
.code
;--------------------------------------------------
; DllEntry: DLL 載入/卸載/執行緒建立銷毀的統一入口
;--------------------------------------------------
DllEntry proc hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD
mov eax, TRUE ; 返回 FALSE 會導致 DLL 載入失敗
ret
DllEntry Endp
;--------------------------------------------------
; OutEdxEax: 將 EDX:EAX (64-bit) 轉為十進位 ASCII 字串
; 參數: lpString - 緩衝區指標 (需足夠大)
; 範例: EDX=0, EAX=01234567h -> "19088743"
;--------------------------------------------------
OutEdxEax proc uses ebx esi edi, lpString:DWORD
mov edi, lpString
mov esi, lpString
mov ecx, 10 ; 除數 10 (十進位)
.while eax != 0 || edx != 0
push eax
mov eax, edx
xor edx, edx
div ecx ; EDX:EAX / 10 -> EAX=商, EDX=餘數
mov ebx, eax ; 暫存高位商
pop eax
div ecx ; 低位除以 10
add dl, '0' ; 餘數轉 ASCII
mov [edi], dl
inc edi
mov edx, ebx ; 迴圈處理高位
.endw
mov byte ptr [edi], 0 ; 字串結束符
dec edi
; 反轉字串 (低位在前 -> 高位在前)
.while edi > esi
mov al, [esi]
xchg al, [edi]
mov [esi], al
inc esi
dec edi
.endw
ret
OutEdxEax endp
end DllEntry
LIBRARY MyDll ; DLL 實際檔名基礎
EXPORTS OutEdxEax ; 導出符號,供外部 GetProcAddress 或靜態連結使用
OutEdxEax PROTO :DWORD ; 一個參數:緩衝區指標
MyDll.asm,按 Ctrl+1 (或「工具 → 編譯&鏈接&運行」)。MyDll.dll, MyDll.lib, MyDll.exp。MyDll.inc 複製到 \masm32\include\MyDll.lib 複製到 \masm32\lib\MyDll.dll 放置於呼叫端 .exe 同目錄或系統路徑下.386
.model flat, stdcall
option casemap:none
include windows.inc
include mydll.inc ; 引入原型
include masm32.inc
include kernel32.inc
includelib mydll.lib ; 連結導入庫
includelib masm32.lib
includelib kernel32.lib
.data?
CharOut db 100 dup(?) ; 接收字串緩衝區
.code
start:
mov edx, 12345678h
mov eax, 87654321h
invoke OutEdxEax, addr CharOut ; 呼叫自訂 DLL 函式
invoke StdOut, addr CharOut ; 輸出: 1311768467139281697
invoke ExitProcess, NULL
end start
| Reason 值 | 意義 | 典型用途 |
|---|---|---|
| DLL_PROCESS_ATTACH | 進程載入 DLL | 初始化資源、建立執行緒區域儲存 (TLS) |
| DLL_PROCESS_DETACH | 進程卸載 DLL | 釋放資源、關閉檔案代碼 |
| DLL_THREAD_ATTACH | 進程內新執行緒建立 | 執行緒專屬初始化 |
| DLL_THREAD_DETACH | 執行緒結束 | 清理 TLS 資料 |
本範例使用隱式連結(includelib mydll.lib + invoke OutEdxEax),連結器自動處理載入。若需延遲載入或條件載入,可改用 LoadLibrary + GetProcAddress (顯式連結),參考下一篇文章「動態庫的執行時間」。
OutEdxEax 不檢查緩衝區長度,64 位整數最大 20 位數字 + 結束符,建議給 32 位元組以上。stdcall,參數由被呼叫者清理堆疊,proto 宣告必須一致。