動態連結庫(DLL)是 Windows 程式設計實現模組化、程式碼重用、跨語言互操作的核心機制。本文針對 C++Builder 環境,系統說明 DLL 專案建立、匯出函數宣告、呼叫慣例選擇、靜態連結(Import Library)與動態載入(LoadLibrary/GetProcAddress) 兩種使用方式,並釐清專案選項對執行階段套件相依性的影響。
#include <vcl.h>
#pragma hdrstop
//---------------------------------------------------------------------------
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) {
return 1; // TRUE 表示初始化成功
}
//---------------------------------------------------------------------------
// 使用者自訂匯出函數寫在這裡
extern "C" __declspec(dllexport) 防止 C++ 名稱修飾,並明確指定呼叫慣例。// MyDll.h
#ifdef MYDLL_EXPORTS
#define MYDLL_API extern "C" __declspec(dllexport)
#else
#define MYDLL_API extern "C" __declspec(dllimport)
#endif
MYDLL_API int __stdcall Add(int a, int b);
MYDLL_API void __stdcall GetVersion(char* buf, int len);
// MyDll.cpp
#include "MyDll.h"
MYDLL_API int __stdcall Add(int a, int b) { return a + b; }
MYDLL_API void __stdcall GetVersion(char* buf, int len) {
strncpy(buf, "1.0.0", len); buf[len-1] = '\0';
}
| 關鍵字 | 參數傳遞 | 堆疊清理 | 適用場景 |
|---|---|---|---|
__cdecl | 右向左壓棧 | 呼叫者 | C/C++ 預設、可變參數 |
__stdcall | 右向左壓棧 | 被呼叫者 | Win32 API、COM、跨語言(VB、C#、Delphi) |
__fastcall | 前兩參數用暫存器 (EAX, EDX) | 被呼叫者 | Borland 內部優化、非公開介面 |
__pascal | 左向右壓棧 | 被呼叫者 | 舊版 Windows API、已淘汰 |
建議:公開介面統一使用 __stdcall 確保跨編譯器/語言相容。
預設專案會連結動態 RTL 與執行階段套件,導致 DLL 必須部署 rtlxx.bpl、vclxx.bpl 等巨大檔案。必須關閉:
編譯後產出獨立 MyDll.dll + MyDll.lib(Import Library),單檔部署零相依。
MyDll.libMyDll.h(或手寫 dllimport 宣告)int sum = Add(3, 5);優點:語法簡潔、編譯期型別檢查、除錯支援完整。缺點:啟動時必須找到 DLL,否則載入失敗。
typedef int (__stdcall *PFN_Add)(int, int);
typedef void (__stdcall *PFN_GetVersion)(char*, int);
HINSTANCE hDll = LoadLibrary(_T("MyDll.dll"));
if (!hDll) { ShowMessage("載入失敗"); return; }
PFN_Add pAdd = (PFN_Add)GetProcAddress(hDll, "Add");
PFN_GetVersion pVer = (PFN_GetVersion)GetProcAddress(hDll, "GetVersion");
if (pAdd && pVer) {
int sum = pAdd(10, 20);
char ver[32]; pVer(ver, sizeof(ver));
ShowMessage(AnsiString("Sum=") + sum + ", Ver=" + ver);
}
FreeLibrary(hDll);
優點:可延遲載入、支援外掛架構、DLL 不存在不會阻塞主程式啟動。缺點:需手寫函數指標、失去編譯期檢查、錯誤易發生於拼字。
implib 與 tlib.dll 無 .lib:implib MyDll.lib MyDll.dll 產生 Import Librarytdump -oi MyDll.dll 或 tlib MyDll.lib, MyDll.lst| 問題 | 原因 | 對策 |
|---|---|---|
| Linker 錯誤 "Unresolved external" | 未加入 .lib 或宣告與定義呼叫慣例不符 | 檢查專案管理器、統一 __stdcall |
| 執行期 "Entry Point Not Found" | C++ 名稱修飾導致匯出名稱不符 | 強制 extern "C"、用 tdump 驗證匯出名 |
| 跨 DLL 記憶體管理崩潰 | 各自擁有獨立堆疊,new/delete 不可跨界 | 提供 DllFreeMem 或使用共用記憶體管理器 |
| VCL 物件跨 DLL 傳遞 | 類別身分識別依賴 RTTI,動態套件模式下才安全 | 介面傳遞 HWND/HANDLE/POD 結構,避免傳 VCL 物件 |