#ifdef BUILDING_MYDLL
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
// MyDLL.h
#ifdef __cplusplus
extern "C" {
#endif
MYDLL_API int __stdcall Add(int a, int b);
MYDLL_API void __stdcall ShowMessage(const char* msg);
#ifdef __cplusplus
}
#endif
// MyDLL.cpp
#include "MyDLL.h"
#include
#pragma hdrstop
int __stdcall Add(int a, int b) { return a + b; }
void __stdcall ShowMessage(const char* msg) { ::MessageBoxA(NULL, msg, "DLL", MB_OK); }
// MyClass.h
class MYDLL_API __stdcall MyClass {
public:
MyClass();
~MyClass();
void DoSomething();
private:
int m_data;
};
// MyClass.cpp
#include "MyClass.h"
MyClass::MyClass() : m_data(0) {}
MyClass::~MyClass() {}
void MyClass::DoSomething() { m_data++; }
implib.exe 由 DLL 產生 .lib 匯入庫:
implib MyDLL.lib MyDLL.dll
.lib 加入應用程式專案 (Project → Add to Project)。dllimport),直接呼叫函式/建立類別實例。
// 應用程式
typedef int (__stdcall *PFN_ADD)(int, int);
typedef void (__stdcall *PFN_SHOW)(const char*);
HINSTANCE hDll = LoadLibrary(_T("MyDLL.dll"));
if (hDll) {
PFN_ADD pAdd = (PFN_ADD)GetProcAddress(hDll, "Add");
PFN_SHOW pShow = (PFN_SHOW)GetProcAddress(hDll, "ShowMessage");
if (pAdd && pShow) {
int sum = pAdd(3, 5); // 8
pShow("Hello from DLL!");
}
FreeLibrary(hDll);
}
關鍵:DLL 與 EXE 必須共用同一個 VCL Application 物件,否則視窗無法正確嵌入 MDI 框架、工具列合併失敗、快捷鍵無效。
// DLL 端
TApplication* SavedApp = NULL;
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
{
if (reason == DLL_PROCESS_DETACH && SavedApp)
Application = SavedApp; // 還原
return 1;
}
extern "C" __declspec(dllexport) __stdcall void CreateMDIChild(TApplication* MainApp, const char* Caption)
{
if (!SavedApp) { SavedApp = Application; Application = MainApp; }
TForm1* frm = new TForm1(Application); // 傳遞 Application 給建構函式
frm->Caption = Caption;
frm->Show();
}
// EXE 端呼叫
typedef void (__stdcall *PFN_CREATE)(TApplication*, const char*);
HINSTANCE hDll = LoadLibrary("ChildDLL.dll");
if (hDll) {
PFN_CREATE pCreate = (PFN_CREATE)GetProcAddress(hDll, "CreateMDIChild");
if (pCreate) pCreate(Application, "子視窗標題");
}
| 呼叫慣例 | BCB 匯出名稱 | VC++ 匯出名稱 | 參數傳遞/清理堆疊 |
|---|---|---|---|
__cdecl | _FuncName | _FuncName | 呼叫者清理,參數右至左入堆疊 |
__stdcall | FuncName | _FuncName@N (N=堆疊位元組數) | 被呼叫者清理,參數右至左 |
__fastcall | @FuncName@N | @FuncName@N | 前三參數用 EAX/EDX/ECX,其餘堆疊,被呼叫者清理 |
__pascal | FUNCNAME | 不支援 | 呼叫者清理,參數左至右 |
| 場景 | 建議 |
|---|---|
| 純 C 介面、跨編譯器/語言 | extern "C" __stdcall + 動態載入 + 版本化函式名 (FuncName_V1) |
| BCB 專用、C++ 類別、緊密整合 | 靜態載入 + 匯出類別 + 共用 RTL (勾選 Build with runtime packages) |
| 插件架構、熱插拔 | 動態載入 + 介面類別 (純虛擬) + 工廠函式 CreatePlugin() |
| 呼叫 VC++/Delphi/其他編譯器 DLL | 動態載入 + tdump/dumpbin 確認名稱 + __stdcall 統一慣例 |
| DLL 內使用 VCL 視窗/元件 | 傳遞 EXE 的 Application 指標、DLL_PROCESS_DETACH 還原、共用 RTL |
DLL 開發核心在於呼叫慣例一致、名稱修飾對應、記憶體管理邊界清晰 (誰配置誰釋放、跨模組 new/delete 需共用 RTL)。掌握 tdump/dumpbin、.def 別名、implib、動態載入技巧,即可應對 BCB/VC++/Delphi 混合開發場景。