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

Delphi 撰寫與呼叫 DLL 完全指南(二):靜態與動態載入、參數傳遞、除錯技巧

     編輯:Delphi

DLL 呼叫兩大模式

特性靜態載入動態載入
語法function Foo: Integer; stdcall; external 'MyDll.dll';LoadLibrary/GetProcAddress/FreeLibrary
載入時機進程啟動時執行期呼叫 LoadLibrary 時
缺少 DLL 行為啟動失敗,系統錯誤訊息可捕獲錯誤,彈性處理(延遲載入、備用路徑)
函數更名external 'MyDll.dll' name 'RealName';GetProcAddress(h, 'RealName')
適用場景核心相依、永遠存在的 DLL選用功能、外掛架構、版本相容、減少啟動相依

靜態載入完整範例

DLL 端

library DelphiDLL;

uses SysUtils, Classes;

function TestDll(I: Integer): Integer; stdcall;
begin
  Result := I * 2;
end;

exports
  TestDll;

begin
end.

EXE 端

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.

動態載入完整範例(呼叫 C++ DLL)

C++ DLL 來源

// CppDLL.cpp
extern "C" __declspec(dllexport) int WINAPI TestC(int i)
{
  return i + 10;
}

Delphi 動態呼叫

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,跨語言必須指定。
  • 字串:嚴禁 直接傳遞 Delphi string(AnsiString/UnicodeString)跨 DLL 邊界。使用 PAnsiChar/PWideChar + 長度參數,或 WideString(COM BSTR,自動管理)。
  • 陣列/記錄:傳遞指標 PMyRecord,確保記憶體配置由呼叫者負責(GetMem/LocalAlloc),DLL 僅讀寫。
  • 物件/類別:不可 跨 DLL 傳遞 VCL 物件(TForm、TButton 等),記憶體管理器、RTTI、虛函數表不共享。改用介面(IUnknown 衍生)+ 套件或 COM。
  • 回呼函數:DLL 接受回呼時,回呼必須為 stdcall 全域函數,透過 UserData: Pointer 傳遞上下文。

實用技巧

1. 開發階段先寫單元,再抽離為 DLL

在 EXE 專案中開發、除錯無誤後,將單元移至 DLL 專案,介面保持不變。

2. 避免可視化單元

DLL 不要 uses Forms, Dialogs, Controls 等 VCL 可視化單元,會大幅增加體積(~300KB+)並引發記憶體管理器衝突。僅 uses SysUtils, Classes, Windows 等非視覺單元。

3. 強制 try..finally 釋放資源

DLL 內部配置的記憶體、開啟的控制代碼,必須在導出函數返回前釋放,或提供對應的 FreeXXX 導出函數由呼叫者呼叫。

4. 版本資源

專案選項 → Version Info 勾選,或在 .dpr 加入 {$R *.res} 並提供 .rc 檔,便於部署管理。

5. 除錯 DLL

  1. Run → Parameters → Host Application:設定呼叫 DLL 的 EXE 路徑。
  2. 在 DLL 程式碼設中斷點,按 F9 啟動宿主程式,即可單步進入 DLL。

6. 命名規範

避免與系統 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 可捕獲處理。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved