BPL (Runtime Package) 要求使用者預知具體類別名稱。若需「不知具體類別、僅知基底抽象介面」即可動態載入使用 (插件模式),BPL 無法滿足。解決方案:DLL 導出元類別 (Class Reference)。
type
TMyBaseForm = class(TForm)
protected
function GetTitle: PChar; virtual; abstract;
end;
TMyBaseFormClass = class of TMyBaseForm; // 元類別類型
TMyBaseFormClass 變數可持有 TMyBaseForm 及其任意子類別的類別本身 (而非實例)AClass := TMyBaseForm、AClass := TForm1、AClass := TButton (若繼承關係符合)Form := AClass.Create(Application);
unit BaseIntf;
interface
type
TMyBaseForm = class(TForm)
protected
function GetTitle: PChar; virtual; abstract;
end;
TMyBaseFormClass = class of TMyBaseForm;
implementation end.
library PluginDLL;
uses BaseIntf, FormImpl1, FormImpl2, FormImpl3;
function GetClassCount: Integer; stdcall;
begin Result := 3; end; // 導出類別數量
function GetClassTypeByIndex(Index: Integer; var Cls: TMyBaseFormClass): WordBool; stdcall;
begin
Result := True;
case Index of
0: Cls := TFormImpl1;
1: Cls := TFormImpl2;
2: Cls := TFormImpl3;
else Result := False;
end;
end;
exports GetClassCount, GetClassTypeByIndex;
begin end.
GetTitle 回傳不同字串
var
DllHandle: THandle;
GetCount: function: Integer; stdcall;
GetClass: function(Index: Integer; var Cls: TMyBaseFormClass): WordBool; stdcall;
I, Count: Integer;
Cls: TMyBaseFormClass;
Frm: TMyBaseForm;
Ok: Boolean;
begin
DllHandle := LoadLibrary('PluginDLL.dll');
@GetCount := GetProcAddress(DllHandle, 'GetClassCount');
@GetClass := GetProcAddress(DllHandle, 'GetClassTypeByIndex');
Count := GetCount;
for I := 0 to Count - 1 do begin
if GetClass(I, Cls) then begin
Frm := Cls.Create(Application); // 多態創建
Frm.Caption := Frm.GetTitle; // 多態呼叫
Frm.Show;
end;
end;
end;
| 技術點 | 作用 |
|---|---|
class of TBase | 定義元類別型別,讓類別成為可傳遞的值 |
| DLL 導出函數傳回元類別 | 運行期動態發現並取得具體類別型別 | Build with Runtime Packages | 確保主程式與 DLL 共享相同 VCL 型別資訊,is/as 正常運作 |
| 多態創建/呼叫 | 主程式完全不知具體類別名,僅透過基底介面操作 |
利用 元類別 + DLL 導出函數 + Runtime Packages,Delphi 可實現真正的「介面導向、運行期發現、動態多態」插件架構。這比 BPL 更靈活,不要求使用者預知具體類別名稱,是構建可擴充應用程式的強大技術。