DLL 應用 - 設計可抽換的模組(2)

2008-04-09 04:28:15来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

以下會一步步實作出一個具體而微的範例,你可以把這個範例視為一個基礎的框架(framework),稍加修改就可以運用於實際的專案開發上面。

描述需求

讓我們來簡單地分析一下應用程式的需求,假設原本的開發方式是將所有的程式單元編譯連結成一個可執行檔,現在要將應用程式的各個功能切割為獨立的模組,例如:

                      --- 客戶資料維護作業(Customer.DLL)
主程式(Main.exe)--- --- 產品資料維護作業(Employee.DLL)
                      --- 訂單資料維護作業(Orders.DLL)

其中每個 DLL 都是在使用者執行該項功能的時候才動態載入,而且每個 DLL 裡面至少包含一個 Form,為了有別於一般的 DLL,以下就以 plugin 稱之。我們預期各 plugin DLL 所包含的 Form 會有一些共同的屬性和行為,因此把這些共同點放到一個基礎視窗類別裡面,讓其他 Form 繼承自這個基礎類別。它們的關係看起來像這樣:

每一個維護作業都需要開啟一個視窗,因此主程式的責任之一便是建立並顯示 DLL 裡面的視窗。我們希望每一個維護作業的視窗關閉後才能執行另一個維護作業,所以使用 ShowModal 的方式顯示視窗。做一些簡單的分析之後,可以得到主程式在執行每項作業時所需的共同步驟:

  1. 載入指定的 plugin DLL。
  2. 建立並顯示 plugin DLL 裡面的 Form 物件。
  3. 釋放 Form 物件。
  4. 釋放 plugin DLL。

其中載入與是釋放 plugin DLL 的工作由主程式負責,而前面有提過 DLL 中的物件必須由 DLL 自己來建立,因此建立、顯示以及釋放 Form 物件的工作都由 plugin DLL 來負責提供函式,主程式只要在適當時機去呼叫它們就行了。

主程式

在主程式中加入一個執行 plugin 的方法,此方法需要一個參數指定 DLL 的檔名以便將其載入執行,像這樣:

procedure TMainForm.RunPlugin(const FileName: string);
var
  ADllHandle: THandle;
  APlugin: IPlugin;
  AFormHandle: THandle;
begin
  ADllHandle := SafeLoadLibrary(FileName);
  if ADllHandle <> 0 then
  begin
    APlugin := DllCreatePlugin(ADllHandle, Application.Handle);
    try
      AFormHandle := APlugin.CreateForm(Handle);
      APlugin.ShowModalForm;
      APlugin := nil;
      FreeLibrary(ADllHandle);
    except
      FreeLibrary(ADllHandle);
      raise;
    end;
  end
  else
    ShowMessage(''''無法載入函式庫: ''''   FileName);
end;

從以上程式碼可以看出主程式載入 DLL 之後會呼叫 DllCreatePlugin 來建立 plugin 物件並且取得其介面參考,接著主程式就利用該介面參考來存取 plugin 物件提供的服務,包括建立視窗,顯示視窗等等。很明顯地,IPlugin 介面是主程式和 plugin DLL 之間溝通的橋樑,而且 IPlugin 介面至少要提供下列方法:

CreateForm - 建立 Form 物件
ShowModalForm - 顯示視窗
DestroyForm - 摧毀 Form 物件

眼尖的讀者可能會發現,上面的程式中並沒有呼叫 DestroyForm,而且也沒有呼叫類似 DllDestroyPlugin 的函式來摧毀 plugin 物件,這些物件什麼時候會被釋放掉?

它們是自動被釋放掉的。由於 Form 物件的建立是透過 plugin 物件來完成,所以我打算把摧毀 Form 物件的責任交給 plugin 物件,也就是當 plugin 物件摧毀時會自動將 Form 物件一併釋放掉;而為了簡化摧毀 plugin 物件的動作,我讓 plugin 物件具有自動參考計數的能力,這麼一來只要該物件沒有人使用它(物件的參考計數為 0)就會自動釋放掉了,做法很簡單,只要讓實作 IPlugin 的類別繼承自 TInterfaceObject 就行了,其他細節都由 VCL 幫我們完成了。

LoadLibrary 與 FreeLibrary 也有自己的參考計數,並且用它來決定是否載入及釋放 DLL。也就是說重複呼叫 LoadLibrary(''''A.DLL'''') 並不會將 A.DLL 載入兩次,第二次的呼叫只會遞增參考計數而已;同樣的,FreeLibrary 會遞減 DLL 的參考計數,直到計數為 0 才會真正將 DLL 釋放掉。

接著看 DllCreatePlugin 函式:

type
  TCreatePluginFunc = function (hApp: THandle): IPlugin; stdcall;

const
  SDllCreatePluginFuncName = ''''CreatePlugin'''';

implementation

resourcestring
  sErrorLoadingDLL = ''''無法載入模組!'''';
  sErrorDllProc = ''''無法呼叫 DLL 函式: %s'''';

function DllCreatePlugin(hLib, hApp: THandle): IPlugin;
var
  pProc: TFarProc;
  CreatePluginFunc: TCreatePluginFunc;
begin
  pProc := GetProcAddress(hLib, PChar(SDllCreatePluginFuncName));
  if pProc = nil then
    raise Exception.CreateFmt(sErrorDllProc, [SDllCreatePluginFuncName]);
  CreatePluginFunc := TCreatePluginFunc(pProc);
  Result := CreatePluginFunc(hApp);
end;

DllCreatePlugin 會嘗試從指定的 DLL 模組中呼叫函式 ''''CreatePlugin'''' 來建立 plugin 物件,並且傳回 plugin 物件的介面參考,參數 hLib 是 DLL 代碼,而 hApp 則直接傳遞給 DLL 的CreatePlugin 函式,這個參數的作用稍後會解釋。

至此主程式所需的程式碼大致上已經完成了,接下來看看 DLL 的 CreatePlugin 函式。

DLL 的輸出函式

我們的 plugin DLL 只有輸出一個函式供外界呼叫,就是前面提到的 CreatePlugin,其函式原型為:

function CreatePlugin(hApp: THandle): IPlugin; export; stdcall;

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:界面(FORM)自动生成工具

下一篇:数据库的一种完全面向对象设计模式(包含实例) Rayphrank原创