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

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

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

我交替使用了「建立物件」與「實體化」兩種詞彙,其實它們指的是同一件事情:建立某個類別的實體(instance)。

此技巧對於團隊開發也有好處,你只要公佈 TPlugin 和 TBaseForm 兩個單元,然後告訴組員照下面兩個步驟做就行了:

  1. 從 TBaseForm 衍生一個新類別(可以利用 Delphi 的物件寶庫來簡化這項工作)。
  2. 在這個新類別的單元的 Uses 子句裡加入 TPlugin 類別所屬的單元,並且在初始化階段把類別名稱指定給 g_ConcreteClass 變數。

在這個範例裡面,我們只有一個 TBaseForm 的後代,叫做 TForm1,因此在 TForm1 的單元裡面會有這一段:

uses
  DllExport;  // TPlugin 類別實作放在這個單元裡面

.....

initialization
  g_ConcreteClass := TForm1;

 

TPlugin.Destroy

解構函式會呼叫 DestroyForm 使 Form 物件一併釋放掉,並且還原 DLL 的 application handle:

destructor TPlugin.Destroy;
begin
  DestroyForm;
  Application.Handle := g_DllAppHandle;
  inherited Destroy;
end;

其中 g_DllAppHandle 是一個全域變數,其宣告如下:

var
  g_DllAppHandle: THandle;

而我們必須在 DLL 初始化的時候將 DLL 本身的 application handle 保存起來:

initialization
  g_DllAppHandle := Application.Handle;

其實如果 DLL 專案有用 "Build with runtime package" 選項的話,這個保存及還原 application handle 的動作就可以免了。相反地,若不加上保存及還原的動作,而且 DLL 專案不使用 "Build with runtime package" 選項的話,當 DLL 被釋放時就會發生主視窗也被一併關閉的怪異情形。

擅用原始的力量

到此重要的部分應該都已經提到了,您可能會發現我並沒有對 TBaseForm 多做說明,原因是在這個範例程式中 TBaseForm 並沒有什麼特別之處,只是為日後擴充時預留的一個基礎類別,你也許會想要將各個模組共用的功能和視覺化介面集中在此類別以簡化各模組的撰寫工作,以及讓應用程式有一致的操作方式和行為,這部分每個人的需求不同,就請您自行發揮了。

如果你覺得以上的程式碼過於片段零散,無法獲得整體的概念,建議您直接看範例的原始碼,把範例程式執行一遍以觀察程式運作的過程,不了解的地方再回來文件裡尋找解釋,這樣也許會比較容易些。為了方便閱讀,我也把範例程式中比較重要的兩個單元分別列在表一和表二裡面了。

列表一. DllUtils.pas
unit DllUtils;

interface

uses
  Windows, Messages, SysUtils, Classes, Forms, Controls;

type
  IPlugin = interface
  [''''{D3F4445A-C704-42BC-8283-822541668919}'''']  
    function CreateForm(hMainForm: THandle): THandle;
    procedure DestroyForm;
    function ShowModalForm: Integer;
  end;

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

function DllCreatePlugin(hLib, hApp: THandle): IPlugin;

implementation

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

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

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

end.

 

列表二. DllExport.pas
unit DllExport;

interface

uses Windows, Classes, Forms, DllUtils, BaseFrm;

type
  // Inherited from TInterfacedObject to be reference-counted.
  TPlugin = class(TInterfacedObject, IPlugin)
  private
    FForm: TBaseForm;
  public
    destructor Destroy; override;

    function CreateForm(hMainForm: THandle): THandle;
    procedure DestroyForm;
    function ShowModalForm: Integer;
  end;

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

exports
  CreatePlugin;

var
  g_ConcreteClass: TBaseFormClass := nil;
  g_PluginIntf: IPlugin = nil;
  g_DllAppHandle: THandle;

implementation

uses Dialogs, SysUtils;

function CreatePlugin(hApp: THandle): IPlugin;
begin
  if hApp <> 0 then
    Application.Handle := hApp;     // Sync Application handle.

  if g_PluginIntf = nil then
    g_PluginIntf := TPlugin.Create; 
  Result := g_PluginIntf;           
end;

{ TPlugin }

destructor TPlugin.Destroy;
begin
  DestroyForm;
  Application.Handle := g_DllAppHandle;
  
			   
			   

标签:

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

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

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