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 關閉視窗、釋放實例、回傳結果。UnitEntranceunit 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.
exports 宣告library Official;
uses SysUtils, Classes, UnitOfficialMainForm, UnitOfficeEntrance, ...;
{$R *.res}
exports ShowDLLForm, FreeDLLForm;
begin end.
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;
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;
系統模組表 SystemModuleList(modAlias, modName, modWndClass, modFile, modMemo)記錄 DLL 元資料。啟動時查詢建立 TMyPlugin 實例集合,按需動態載入。
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 卸載僅在應用程式退出時統一執行。
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 斷點調試。
Self.Close 導致 AV。TInterfacedObject 管理視窗生命週期、支援真正的 FreeLibrary 熱卸載。此架構展示 Delphi 以 DLL 實現插件化的完整路徑:契約定義、動態載入、資料庫驅動發現、宿主協作、調試配置。雖存已知缺陷,但為中型系統模組化提供了可落地的起點框架。