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

Delphi 插件化架構實踐:DLL 動態載入、介面契約、資料庫驅動與宿主協作模式

     編輯:Delphi

架構動機

MDI 單體專案隨功能增長編譯緩慢、團隊協作困難。採用 DLL 插件 + 宿主容器 架構:各子系統獨立 DLL 專案,宿主動態載入、統一生命週期管理、參數傳遞與權限控制。

介面契約設計

每個 DLL 導出兩個標準呼叫慣例函數:

function ShowDLLForm(AHandle: THandle; ACaption, AUserID: string): Boolean; stdcall;
function FreeDLLForm(AHandle: THandle; ACaption, AUserID: string): Boolean; stdcall;
  • ShowDLLForm:宿主傳遞主視窗句柄、標題、當前用戶 ID;DLL 掛接 Application.Handle、建立主視窗實例、初始化 UI 權限、Show。
  • FreeDLLForm:宿主通知釋放;DLL 關閉視窗、釋放實例、回傳結果。

DLL 專案關鍵實作

入口單元 UnitEntrance

unit UnitOfficeEntrance;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms;
function ShowDLLForm(AHandle: THandle; ACaption, AUserID: string): Boolean; stdcall;
function FreeDLLForm(AHandle: THandle; ACaption, AUserID: string): Boolean; stdcall;
implementation
uses UnitOfficialMainForm;
var DLL_Form: TFormOfficialMain;

function ShowDLLForm(AHandle: THandle; ACaption, AUserID: string): Boolean;
begin
  Result := True;
  try
    Application.Handle := AHandle; // 掛接宿主
    DLL_Form := TFormOfficialMain.Create(Application);
    try
      with DLL_Form do begin
        Caption := ACaption;
        StatusBar.Panels[0].Text := AUserID; // 權限顯示
        Show;
      end;
    except
      DLL_Form.Free; raise;
    end;
  except
    Result := False;
  end;
end;

function FreeDLLForm(AHandle: THandle; ACaption, AUserID: string): Boolean;
begin
  Application.Handle := AHandle;
  if DLL_Form.Showing then DLL_Form.Close; // 觸發 CloseQuery 可攔截
  if not DLL_Form.Showing then begin
    DLL_Form.Free; Result := True;
  end else Result := False; // 使用者取消關閉
end;
end.

DLL 專案檔 exports 宣告

library Official;
uses SysUtils, Classes, UnitOfficialMainForm, UnitOfficeEntrance, ...;
{$R *.res}
exports ShowDLLForm, FreeDLLForm;
begin end.

宿主容器核心邏輯

1. 函數指標型別與動態載入

type
  TShowDLLForm = function(AHandle: THandle; ACaption, AUserID: string): Boolean; stdcall;
  TFreeDLLForm = function(AHandle: THandle; ACaption, AUserID: string): Boolean; stdcall;

  TMyPlugin = class
    Caption, DllFileName, WndClass, UserID: string;
    LibHandle: THandle;
    ShowProc, FreeProc: Pointer;
  end;

2. 載入與函數綁定

function TFormHall.LoadShowPluginForm(const APlugin: TMyPlugin): Boolean;
var
  ShowDLLForm: TShowDLLForm;
  FreeDLLForm: TFreeDLLForm;
  Path: string;
begin
  Path := ExtractFilePath(Application.ExeName) + 'plugins\' + APlugin.DllFileName;
  APlugin.LibHandle := LoadLibrary(PChar(Path));
  APlugin.FreeProc := GetProcAddress(APlugin.LibHandle, 'FreeDLLForm');
  APlugin.ShowProc := GetProcAddress(APlugin.LibHandle, 'ShowDLLForm');
  @ShowDLLForm := APlugin.ShowProc;
  @FreeDLLForm := APlugin.FreeProc;
  Result := ShowDLLForm(Self.Handle, APlugin.Caption, APlugin.UserID);
end;

3. 資料庫驅動插件發現

系統模組表 SystemModuleList(modAlias, modName, modWndClass, modFile, modMemo)記錄 DLL 元資料。啟動時查詢建立 TMyPlugin 實例集合,按需動態載入。

4. 視窗顯示/隱藏控制(未真正卸載 DLL)

hWnd := FindWindow(PChar(APlugin.WndClass), nil);
if hWnd <> 0 then begin
  if not IsWindowVisible(hWnd) then ShowWindow(hWnd, SW_SHOWDEFAULT)
  else ShowWindow(hWnd, SW_HIDE);
end;

註:直接 Close 會觸發記憶體錯誤,故採用隱藏策略,DLL 卸載僅在應用程式退出時統一執行。

5. 退出時統一卸載

procedure TFormHall.ClosePlugin(APlugin: TMyPlugin);
var FreeDLLForm: TFreeDLLForm;
begin
  if (APlugin.LibHandle = 0) or (APlugin.FreeProc = nil) then Exit;
  @FreeDLLForm := APlugin.FreeProc;
  if not FreeDLLForm(Application.Handle, '', '') then ShowMessage('卸載失敗');
  FreeLibrary(APlugin.LibHandle);
end;

調試技巧

DLL 專案 Run → Parameters → Host Application 設為宿主 EXE,按 F9 即可進入 DLL 斷點調試。

已知不足與改進方向

  • 視窗隱藏而非真正釋放,資源佔用持續。
  • DLL 視窗關閉按鈕必須隱藏,否則 Self.Close 導致 AV。
  • 改進建議:引入介面參考計數、TInterfacedObject 管理視窗生命週期、支援真正的 FreeLibrary 熱卸載。

總結

此架構展示 Delphi 以 DLL 實現插件化的完整路徑:契約定義、動態載入、資料庫驅動發現、宿主協作、調試配置。雖存已知缺陷,但為中型系統模組化提供了可落地的起點框架。

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