二、具體實現
第一步 ,建立一個抽象類 :
我們使用這樣一個簡單的聲明 ,該抽象類只提供了一種抽象方法 ,但並不影 響我們描述問題 :
TMyBaseForm = Class(TForm)
protected
function GetTitle: pchar; virtual; abstract;
end;
MyBaseFormClass = Class of TMyBaseForm;
暫不探討這麼一個抽象類提供了多少可供實用的方法和接口 ,因為我們要討 論的是一種技術上的可行性。假設作者定義此接口的初衷只是希望獲得任意多變 化的 Title,而具體 GetTitle 的返回值是什麼需要靠子類來實現。並且 ,作者 還希望子類的代碼放在 Dll 中實現 ,與主程序分離 -- 這樣的方式很有些插件 的味道 ,或許還能實現 Plug&Play 的某些特性 -- 是不是挺吸引人啊?那 麼 ,下一不應該怎麼做呢?
首先主程序和 Dll 程序應當將上述聲明的單元包含進來 ,然後 ,主程序負責 實現一個驅動 -- 動態加載 Dll,動態加載類 ; 而 Dll 負責實現子類。
先說 Dll 吧 ,Dll 應當做什麼工作?
第二步 ,Dll 中導出子類 :
我們設計了以下兩個導出函數 :
1.function GetClassCount: integer; stdcall;
告訴調用者 ,本 Dll 中共有幾個子類 ;
2.function GetClassTypeByIndex(const iIndex: integer;
var ClassType: MyBaseFormClass): WordBool; stdcall;
以索引方式獲得具體的子類。注意 ,此處的 ClassType 的類型是 MyBaseFormClass,這表明 ,它的值將是一個確定的自 TMyBaseForm 繼承而來的 類。
以下是它們可能的一種實現 :
function GetClassCount: integer;
begin
result := 3; // 表明本 Dll 中導出了 3 個類
end;
function GetClassTypeByIndex(const iIndex: integer;
var ClassType: MyBaseFormClass): WordBool;
begin
result := True;
case iIndex of
0: ClassType := TFrmTest1;
1: ClassType := TFrmTest2;
2: ClassType := TFrmTest3;
else
result := False;
end;
end;
當然 ,在該單元的 Use 列表中應當將 TFrmTest1 、 TFrmTest2 以及 TFrmTest3 所在的單元包含進來。而 TFrmTest1 的實現可以象這樣 :
TFrmTest1 = Class(TMyBaseForm)
protected
function GetTitle: PChar; override;
end;
function TFrmTest1.GetTitle: Pchar;
begin
result := 'Hello from TFrmTest1';
end;
末了 ,別忘了將 GetClassCount 和 GetClassByIndex 加到 Exports 列表中 。然後 ,Build 該 Dll 工程的時候 ,請將 Project option-package 中的 " 使用運行包 use runtime package" 打勾。至於具體的原因後面講。
至此 ,Dll 方面的工作告一段落。