如何實作繼承一個COM介面(Delphi)

//酷小毅的Delphi試鍊場

//作者: 酷小毅

//2002/03/24 於永和

如何實作繼承一個介面

(1).為何談介面的實作繼承

相信現在大家都有清楚的物件導向觀念,在物件導向中我們可以透過繼承來達到讓所封裝的類別資料有高度的Reuse特性,在COM當中則強調將介面與實作分開,這現在已可說是現在物件導向的先驅了,我們知道傳統物件導向對於繼承對SuperClass所產生的相依性不會出現在COM的世界中,而且COM對於封裝做的非常的徹底,COM的Client可說都是以介面在操作,今天筆者將介紹如何在Delphi中實作繼承別人的介面,也許是其他的ActiveX DLL or ActiveX Exe檔案,實作繼承的目的在於繼承該介面原始的行為模式,公開的介面產生它就是一個以OLE Automation技術為基礎的OLE Server,如同MS Word and Excel等等,Automation技術指的實際上就是實作IDispatch的COM物件,實作IDispatch介面又稱為雙介面(Dual Interface),目前你眼睛所看的見的Com Object 或ActiveX元件DLL, OCX等都屬於Dual Interface的物件了.

實作繼承使我們的OLE Server提供相同的介面給外界使用,那當然第一步要有原始介面囉,我們 先建立一個ActiveX Library, 接著建立一個COM Object,接著Delphi會先請你為這個CoClass取個名子,筆者取名InterfaceTest,接著我們就可在Delphi提供的TypeLibrary編輯器建立如下的Method...

image

整個COM Object會提供一個對外的Method,這個Method只是一個空的沒有任何程式碼的Method,因為我們將在ImplementClient的程式中去Implement它,這個空的COM Object Unit1程式碼如下所示 :

//Com Object程式碼如下

unit Unit1;

interface

uses
Windows, ActiveX, Classes, ComObj, PrjInterfaceTest_TLB, StdVcl;

type
TInterfaceTest = class(TTypedComObject, IInterfaceTest)
private
BtnCount: Integer;
protected
function CreateButton: HResult; stdcall;
function Get_Count: Integer; stdcall;
function Set_Count(Value: Integer): HResult; stdcall;
{Declare IInterfaceTest methods here}
end;

implementation

uses ComServ;

function TInterfaceTest.CreateButton: HResult;
begin
//提供給其他程式Implement用
end;

function TInterfaceTest.Get_Count: Integer;
begin
Result := BtnCount;
end;

function TInterfaceTest.Set_Count(Value: Integer): HResult;
begin
BtnCount := Value;
end;

initialization
TTypedComObjectFactory.Create(ComServer, TInterfaceTest, Class_InterfaceTest,
ciMultiInstance, tmApartment);
end.

(2).建立ImplementClient程式

現在我們建立一個空的Application,接著在建立一個Automation Object,這就是一個OLE Server/OLE Automation技術最少會提供一個CoClass服務其他程式,所以會再看到一個TypeLibrary的編輯器,現在我們就要在本身的CoClass中去Impement 剛才我們建立的InterfaceTest的介面,首先我們必須先從選單Project->Import TypeLibrary將InterfaceTest的TypeLibrary import進來,然後在TypeLibrary的PrjImpClient裡的Uses頁次也將InterfaceTest引用進來..如下圖:

圖(二)

image

然後在CoClass(CImpClient)裡的Implements頁次中將InterfaceTest介面Implement進來..如圖三

圖(三)

image

在現在這個CImpClient程式中主要是一個提供service的一個OLE Server,它本身繼承實作了COM Object中的CreateButton方法,然後在本身OLE Server中再真正的去實作CreateButton所要做的動作,其實整個程式的結構很簡單,完整程式碼如下 : (注意:當然Delphi所Import建立的TypeLibrary的TLB Object Pascal 宣告部分我就不列出了,筆者會提供完整範例程式下載)

//CImpClient OLE Server Unit1完整程式碼
unit Unit2;

interface

uses
ComObj, ActiveX, PrjImpClient_TLB, StdVcl, PrjInterfaceTest_TLB, Unit1, Buttons;

type
TCImpClient = class(TAutoObject, ICImplementClient, IInterfaceTest)
private
BtnCount: Integer;
protected
function CreateButton: HResult; stdcall; //實作IInterfaceTest介面的CreateButton方法
function Get_Count: Integer; stdcall;
function Set_Count(Param1: Integer): HResult; stdcall;
procedure Create1; safecall;
//本身再提供一個介面方法來提供給外部呼叫
{ Protected declarations }
public
frm1: TForm1;

end;

implementation

uses ComServ;

procedure TCImpClient.Create1;
begin
CreateButton;
end;

//實作Create Button的行為
function TCImpClient.CreateButton: HResult;
begin
frm1 := TForm1.Create(nil);
frm1.BtnButton1 := TBitBtn.Create(nil);
frm1.BtnButton1.parent := Form1;
frm1.BtnButton1.Top := 30;
frm1.BtnButton1.Left := 10;
frm1.BtnButton1.Width := 200;
frm1.BtnButton1.Caption := '嗨!我是Imp Client建立的按鈕';
end;

function TCImpClient.Get_Count: Integer;
begin
Result := BtnCount;
end;

function TCImpClient.Set_Count(Param1: Integer): HResult;
begin
BtnCount := Param1;
end;

initialization
TAutoObjectFactory.Create(ComServer, TCImpClient, Class_CImpClient,
ciMultiInstance, tmApartment);
end.

(3).實作OLE Server的Client程式

現在我們要來玩一個很有趣的遊戲,同樣的建立一個一般的Application,不過不須為Automation Object,因為它只是單純的OLE Server的Client程式而以,不過我們必須Import CimpClient這個OLE Server的TypeLibrary,然後建立這個介面物件ICImplementClient再呼叫Createremote()方法,其實COM的這項技術已經是跨行程的呼叫了,這個就程式碼非常簡單..

//OLE Server Client完整程式碼

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, PrjImpClient_TLB,
StdCtrls, Buttons;

type
TForm1 = class(TForm)
BitBtn1: TBitBtn;
procedure BitBtn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.BitBtn1Click(Sender: TObject);
var
New_CImp: ICImplementClient;
begin
New_CImp := CoCImpClient.CreateRemote('gelis');
//建立遠端物件
New_CImp.Create1;
//呼叫OLE Server CImpClient 的Create Button方法
end;

end.

有趣的地方來的,當我們將實作介面的CImpClient和一般的client 同時Run起來後,當我們按下Client畫面的按鈕後,你會看見CImpClient的畫面上會自動建立出一個按鈕,所以透過這項技術client可以呼叫OLE Server來幫忙做一些事情,如呼叫MS Word來幫忙我們產生報表(Word本身就是一個OLE Server),關於呼叫Word製作報表的相關技術可參考筆者的如何取得Word Automation所提供的服務

圖(四)

image

圖(五).叫CImpClient OLE Server建立Button的情形

image

範例程式碼下載

(1).PrjInterfaceTest 提供實作繼承的基礎介面

(2).ImplementClient 實作的Client

(3).呼叫ImplementClient建立按鈕的Client.(一般執行檔)

留言

這個網誌中的熱門文章

軟體工程師 - 成長的 10 個階段

常見的程式碼壞味道(Code Smell or Bad Smell)

什麼是 gRPC ?