| 特性 | 靜態載入 | 動態載入 |
|---|---|---|
| 語法 | function Foo: Integer; stdcall; external 'MyDll.dll'; | LoadLibrary/GetProcAddress/FreeLibrary |
| 載入時機 | 進程啟動時 | 執行期呼叫 LoadLibrary 時 |
| 缺少 DLL 行為 | 啟動失敗,系統錯誤訊息 | 可捕獲錯誤,彈性處理(延遲載入、備用路徑) |
| 函數更名 | external 'MyDll.dll' name 'RealName'; | GetProcAddress(h, 'RealName') |
| 適用場景 | 核心相依、永遠存在的 DLL | 選用功能、外掛架構、版本相容、減少啟動相依 |
library DelphiDLL;
uses SysUtils, Classes;
function TestDll(I: Integer): Integer; stdcall;
begin
Result := I * 2;
end;
exports
TestDll;
begin
end.
unit Unit1;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
// 靜態宣告
function TestDll(I: Integer): Integer; stdcall; external 'DelphiDLL.dll';
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text := IntToStr(TestDll(1)); // 呼叫如同本地函數
end;
end.
// CppDLL.cpp
extern "C" __declspec(dllexport) int WINAPI TestC(int i)
{
return i + 10;
}
procedure TForm1.Button1Click(Sender: TObject);
type
TIntFunc = function(I: Integer): Integer; stdcall;
var
hMod: THandle;
Proc: Pointer;
Func: TIntFunc;
begin
hMod := LoadLibrary('CppDLL.dll');
if hMod = 0 then
begin
ShowMessage('載入 CppDLL.dll 失敗:' + SysErrorMessage(GetLastError));
Exit;
end;
try
Proc := GetProcAddress(hMod, 'TestC');
if Proc = nil then
begin
ShowMessage('找不到 TestC 函數');
Exit;
end;
Func := TIntFunc(Proc);
Edit1.Text := IntToStr(Func(1)); // 結果應為 11
finally
FreeLibrary(hMod); // 必須釋放,否則 DLL 計數不歸零
end;
end;
stdcall(Win32 API 標準),Delphi 預設 register,跨語言必須指定。string(AnsiString/UnicodeString)跨 DLL 邊界。使用 PAnsiChar/PWideChar + 長度參數,或 WideString(COM BSTR,自動管理)。PMyRecord,確保記憶體配置由呼叫者負責(GetMem/LocalAlloc),DLL 僅讀寫。TForm、TButton 等),記憶體管理器、RTTI、虛函數表不共享。改用介面(IUnknown 衍生)+ 套件或 COM。stdcall 全域函數,透過 UserData: Pointer 傳遞上下文。在 EXE 專案中開發、除錯無誤後,將單元移至 DLL 專案,介面保持不變。
DLL 不要 uses Forms, Dialogs, Controls 等 VCL 可視化單元,會大幅增加體積(~300KB+)並引發記憶體管理器衝突。僅 uses SysUtils, Classes, Windows 等非視覺單元。
try..finally 釋放資源DLL 內部配置的記憶體、開啟的控制代碼,必須在導出函數返回前釋放,或提供對應的 FreeXXX 導出函數由呼叫者呼叫。
專案選項 → Version Info 勾選,或在 .dpr 加入 {$R *.res} 並提供 .rc 檔,便於部署管理。
避免與系統 DLL 衝突:採用 Company_Module_Function.dll 格式,如 MyApp_Core_Utils.dll。
function DelayedLoad(const DllName, ProcName: string): Pointer;
var
hMod: THandle;
begin
Result := nil;
hMod := LoadLibrary(PChar(DllName));
if hMod <> 0 then
Result := GetProcAddress(hMod, PChar(ProcName));
// 注意:此處未 FreeLibrary,若頻繁呼叫應快取 hMod
end;
Delphi 2010+ 支援 delayed 關鍵字:
function Foo: Integer; stdcall; external 'MyDll.dll' delayed;
首次呼叫時自動載入,失敗拋出 ESystemError 可捕獲處理。