CreatePlugin 函式會建立 TPlugin 物件並且傳回 IPlugin 介面的參考。由於 plugin 物件僅需被建立一次,我們可以用一個全域變數實作出簡單的 Singleton(3):
var
g_PluginIntf: IPlugin = nil;
implementation
function CreatePlugin(hApp: THandle): IPlugin;
begin
Application.Handle := hApp; // 讓 EXE 與 DLL 使用同一個 application handle.
if g_PluginIntf = nil then
g_PluginIntf := TPlugin.Create; // TPlugin 的物件參考計數 = 1
Result := g_PluginIntf; // TPlugin 的物件參考計數 = 2
end;
CreatePlugin 需要傳入一個參數 hApp,代表呼叫者程序的 Application 物件的 Handle,通常是傳入 Application.Handle,好讓主程式和 DLL 的 Application 物件能夠「同步」。之所以要這麼做是因為當你的 DLL 專案未使用 "Build with runtime package" 選項時,執行檔和載入的 DLL 會各自有一個 Application 物件,但是只有執行檔的 Application 物件有連結一個視窗,DLL 則沒有,因此 DLL 的 Application.Handle 屬性總是為 0。若少了這個同步的動作,那麼當 DLL 的 Form 開啟時,你會在桌面的工作列上看到多了一個視窗按鈕,看起來就像執行了另一個應用程式一樣,我們不希望看到這種情形。
當然啦,如果你的主程式和 DLL 都使用 "Build with runtime packages" 來建立(你應該這麼做),就不需要這個同步動作了(想想看為什麼?)。
程式碼裡面有兩行關於物件參考計數的註解,是想要表達介面程式設計的一個基本觀念:當一個介面參考在函式之間以 pass by value 的方式傳遞時會遞增物件的參考計數(pass by reference 則不會)。此觀念有助於你正確掌握物件的壽命。
最後別忘了還要把這輸出函式加到專案原始碼的 exports 子句裡頭:
exports CreatePlugin;
IPlugin 介面與 TPlugin 類別
IPlugin 介面定義如下:
IPlugin = interface
[''''{D3F4445A-C704-42BC-8283-822541668919}''''] // 按 Ctrl Shift G 產生 GUID
function CreateForm(hMainForm: THandle): THandle;
procedure DestroyForm;
function ShowModalForm: Integer;
end;
其實以上函式也可以寫成一般的 DLL 函式,當作是 DLL 的介面,之所以另外定義這個介面,一方面是希望簡化 DLL 本身的介面,另一方面也可以集中管理程式碼,以後如果需要增加介面方法的話,只要加在 IPlugin 介面裡面就好了,不用把現有的 DLL 原始碼一個個找出來修改,這也有助於簡化 DLL 的撰寫以及日後的維護工作。
介面不包含實作,實作必須由類別來提供。接著定義一個 TPlugin 類別來實作 IPlugin 介面:
TPlugin = class(TInterfacedObject, IPlugin) private FForm: TForm; public destructor Destroy; override; function CreateForm(hMainForm: THandle): THandle; procedure DestroyForm; function ShowModalForm: Integer; end;
在 IPlugin 中加上 GUID 以及讓 TPlugin 繼承自 TInterfacedObject 的目的,是為了讓物件擁有 Interfaced RTTI 以及自動參考計數的能力,這樣我們的 TPlugin 物件就會在沒有任何人使用它時自動釋放掉。私有成員 FForm 記錄了此 plugin 物件所建立的視窗的參考,以便控制其壽命,其型態也可以視需要改成 TBaseForm,那麼你的 TBaseForm 的設計得盡量不要經常修改,或者說設計得抽象一些,讓這些核心的類別在比較抽象的層次上面運作。
各個方法的名稱皆可望文生義,程式碼也很簡單,相信你可以猜個八九不離十,這裡就不一一列出,比較值得一提的是 CreateForm 函式與解構元 Destroy,分述如下:
TPlugin.CreateForm - 使用類別參考來建立物件
在 CreateForm 函式裡面,建立 Form 物件的那行程式是這麼寫的:
FForm := g_ConcreteClass.Create(Application);
其中 g_ConcreteClass 是一個全域變數,其定義為:
var g_ConcreteClass: TBaseFormClass := nil;
而 TBaseFormClass 是一個類別參考型態(class-reference type),它跟 TBaseForm 定義在同一個單元裡面:
type TBaseFormClass = class of TBaseForm; TBaseForm = class(TForm) ..... end;
也就是說我們用一個類別參考型態的變數 g_ConcreteClass 來記錄欲實體化的類別型態,因此在建立 Form 物件之前還必須先設定 g_ConcreteClass 才行,如此 TPlugin 才能以正確的 Form 類別來進行實體化的動作。
您或許會想為什麼要這麼麻煩,直接寫成像 TCustomerForm.Create 這樣不就好了嗎?
簡單地說,是基於維護的考量。由於在整個 TPlugin 的實作裡面,日後唯一可以能會經常變動的就是要被實體化的 Form 類別,使用類別參考使我們免於在 TPlugin 的實作程式碼裡面把類別型態寫死,以後如果要實體化其他的 Form 類別,只要修改 g_ConcreteClass 這個變數就行了,不用再費一番搜尋及替換文字的功夫,還得擔心有沒有哪裡沒有改到;換句話說,我們等於使用類別參考來讓編譯器幫我們完成這個替換文字的動作,而且保證不會遺漏任何地方。
文章整理:西部数码--专业提供域名注册、虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!




