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

從零手寫 MASM32 動態連結庫 (DLL):導出函式、模組定義與應用程式呼叫全攻略

     編輯:組合語言

為什麼要自己寫 DLL?

在 Windows 程式開發中,我們每天都在呼叫系統 DLL(kernel32.dll, user32.dll)裡的函式。但若能將常用演算法、專有邏輯封裝成自訂 DLL,不僅能實現程式碼重複使用、減小主程式體積,還能支援多語言模組化、熱插拔擴充等進階架構。本文將手把手教您使用 MASM32 從零建立一個 DLL,導出 OutEdxEax 函式(將 64 位整數 EDX:EAX 轉為十進位字串),並撰寫測試程式呼叫驗證。

DLL 專案三大核心檔案

一個標準的 MASM32 DLL 專案通常包含:

  • 原始碼:實作 DllEntry 與導出函式。
  • 模組定義檔:宣告 DLL 名稱與導出符號。
  • 標頭檔:供呼叫端 includelib 與 proto 使用。

1. MyDll.asm — 核心邏輯與進入點

; 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

2. MyDll.def — 模組定義檔 (必須與 .asm 同目錄)

LIBRARY MyDll          ; DLL 實際檔名基礎
EXPORTS OutEdxEax      ; 導出符號,供外部 GetProcAddress 或靜態連結使用

3. MyDll.inc — 呼叫端引用標頭

OutEdxEax PROTO :DWORD   ; 一個參數:緩衝區指標

編譯與部署流程

  1. 使用 EditPlus 開啟 MyDll.asm,按 Ctrl+1 (或「工具 → 編譯&鏈接&運行」)。
  2. 成功後產生檔案:MyDll.dll, MyDll.lib, MyDll.exp。
  3. 部署關鍵步驟:
    • 將 MyDll.inc 複製到 \masm32\include\
    • 將 MyDll.lib 複製到 \masm32\lib\
    • 將 MyDll.dll 放置於呼叫端 .exe 同目錄或系統路徑下

呼叫端測試程式:10.asm

.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

進階觀念補充

DllEntry 的四大觸發時機

Reason 值意義典型用途
DLL_PROCESS_ATTACH進程載入 DLL初始化資源、建立執行緒區域儲存 (TLS)
DLL_PROCESS_DETACH進程卸載 DLL釋放資源、關閉檔案代碼
DLL_THREAD_ATTACH進程內新執行緒建立執行緒專屬初始化
DLL_THREAD_DETACH執行緒結束清理 TLS 資料

隱式連結 vs 顯式連結

本範例使用隱式連結(includelib mydll.lib + invoke OutEdxEax),連結器自動處理載入。若需延遲載入或條件載入,可改用 LoadLibrary + GetProcAddress (顯式連結),參考下一篇文章「動態庫的執行時間」。

常見坑點提醒

  • 緩衝區太小:OutEdxEax 不檢查緩衝區長度,64 位整數最大 20 位數字 + 結束符,建議給 32 位元組以上。
  • 呼叫慣例不匹配:MASM32 預設 stdcall,參數由被呼叫者清理堆疊,proto 宣告必須一致。
  • DLL 路徑問題:測試時最簡單將 DLL 放在 .exe 同目錄;正式部署請遵循 Windows DLL 搜尋順序。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved