1. 程式人生 > >Dll中匯出類--Delphi實戰之一

Dll中匯出類--Delphi實戰之一

 

Dll中匯出類--Delphi實戰之一<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

作者: Musicwind®

建立時間: 2001-11-01

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

更新歷史:No.1

更新時間:2001-11-01 20:09

更新人員:Musicwind®

更新備註:初稿完成。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

摘要:

本文討論的是如何在Dll中匯出類——基於某個特定的抽象類的許多子類。這種技術運用了多型的思想,使得我們得到類似於外掛的效果。

預期讀者:

瞭解多型的概念;瞭解元類的概念。

技術難度:

6/10

Dll中匯出類,想必大家首先想到的是使用bpl包。這種方式有一個不好,那就是使用者必須清楚這個包中含有那些類,也就是說必須知道類的名字——這在一定意義上是個限制,試想一種情況,使用者定義了一個底層的抽象類(abstract class),然後在此基礎上定義了許多應用類(concrete class),那麼,對於使用者來說,他希望在不知道具體有哪些類的情況下能使用這些類——這麼說似乎有些玄,但實際情況確實如此,因為定義抽象類的時候並不能預料到以後會有多少個具體類——那麼這樣的需求,要靠什麼樣的技術來實現呢?

其實實現的技術的難度並不大——作者在此將自己實踐的經驗獻給大家,算作拋磚引玉,希望能看到其他更好的方法!

以下先介紹該方法涉及的一些基礎知識,然後用一個例子來說明具體的實現。

一、基本概念

元類(meta class),也叫類引用型別(class-reference type),可以看成是一種類的型別,以該型別宣告的變數的值代表一個類。比如:

type

TClass = Class of TObject

這樣就聲明瞭一個元類的型別。然後可以有這樣的變數宣告:

Var

AClass: TClass;

那麼,就可以有這樣的用法:

AClass := TObject;

或者:

AClass := TButton;

或者:

AClass := TForm;

等等。

因為TClass是一個TObject型別的元類,而TButtonTForm等都是自TObject派生而來,因而TButtonTForm這樣的值對於AClass都是可接受的。

然後,我們就可以運用多型的思想,靈活運用AClass這個類變量了。而這一點也正是下文具體實現的基礎知識。

二、具體實現

第一步,建立一個抽象類:

我們使用這樣一個簡單的宣告,該抽象類只提供了一種抽象方法,但並不影響我們描述問題:

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中共有幾個子類;

2function 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列表中應當將TFrmTest1TFrmTest2以及TFrmTest3所在的單元包含進來。而TFrmTest1的實現可以象這樣:

TFrmTest1 = Class(TMyBaseForm)

protected

function GetTitle: PChar; override;

end;

function TFrmTest1.GetTitle: Pchar;

begin

result := ‘Hello from TFrmTest1’

end;

末了,別忘了將GetClassCountGetClassByIndex加到Exports列表中。然後,BuildDll工程的時候,請將Project option-package 中的使用執行包use runtime package”打勾。至於具體的原因後面講。

至此,Dll方面的工作告一段落。

第三步,主程式驅動引擎的實現:

這一步相對來說容易些——無非是動態載入Dll,然後呼叫GetClassCount函式,接著呼叫GetClassByIndex。關鍵的程式碼:

Var AClass: TMyBaseClass;

AForm: TMyBaseForm;

I, iCount: integer;

blResult: Boolean;

begin

//略去載入動態庫的部分,假定FPGetClassProc指向GetClassCount函式,FPGetClassByIndexProc指向GetClassByIndex,則:

iCount := FPGetClassProc;

for I := 0 to iCount – 1 do

begin

AClass := FPGetClassByIndex(I, blResult);

if blResult then

begin

AForm := AClass.Create(Application);

AForm.Caption := AForm.GetTitle;

AForm.Show;

end;

end;

//…

end;

注意一點,和Dll相似,建立輸出檔案的時候,也需要選擇使用執行時間包。這是因為,如果不使用執行時間包,將導致相同的類在記憶體中有多個副本,因而對它們使用Is操作符的將返回False的結果。

Musicwind®@HangZhou.Zhejiang.China

2001-11-01

更多文章

[文終]