日期:
OLE: (Object linking and embedding對象的鏈接與嵌入)
使得服務(wù)器模塊和客戶(hù)模塊通過(guò)標準的接口進(jìn)行通訊。兩個(gè)模塊可以在同一臺計算機或位于不同的計算機上,位置對用戶(hù)來(lái)說(shuō)是無(wú)關(guān)緊要的。服務(wù)器模塊實(shí)現了一組接口,客戶(hù)模塊通過(guò)這組接口進(jìn)行通訊。
COM: (Component object modal組件對象模型)
實(shí)現了OLE的功能,具體可完成一下功能:
l 編寫(xiě)供多種語(yǔ)言使用的代碼;
多種語(yǔ)言:指的是建立好的COM組件不在乎訪(fǎng)問(wèn)它的編程語(yǔ)言,任何一種編程語(yǔ)言只要知道COM組件的接口,訪(fǎng)問(wèn)是都能完成同樣的功能。
l 創(chuàng )建ActiveX控件;
l 通過(guò)OLE Automation操縱其他應用程序;
如:Microsoft Excel的OLE編程接口,創(chuàng )建對象后,任何一種程序都可以實(shí)現對Excel的操作。
l 與其他計算機上的應用程序通訊;
實(shí)際為COM接口與接口之間的通訊,因其實(shí)現了不同語(yǔ)言、不同計算機的方式,所以實(shí)現不同計算機上應用程序的通訊也就十分容易。
COM模塊:
COM的模塊指獨立的應用程序(EXE)或者動(dòng)態(tài)連接庫(DLL),在實(shí)現COM時(shí),采用DLL方式要比較容易一些。因為:應用程序在加載時(shí)在內存中都是獨立的地址控件,而DLL加載后可以駐留內存。當多個(gè)客戶(hù)端調用COM時(shí),如果采用EXE形式,就會(huì )有多個(gè)EXE被加載,而且COM處理客戶(hù)端的訪(fǎng)問(wèn)時(shí),也必須在不同的地址空間來(lái)回切換,大費周張。而DLL形式永遠只有一個(gè)駐留內存,COM只要在相同的內存空間中尋找執行代碼即可。
DLL我們都已經(jīng)寫(xiě)了很多,通常我們寫(xiě)的DLL都是定義的一些方法或是過(guò)程來(lái)實(shí)現特定的操作,當然定義的出口(Exports)也就是這些方法或是過(guò)程?,F在我們來(lái)寫(xiě)一個(gè)出口為一個(gè)類(lèi)的DLL來(lái)展開(kāi)我們COM編寫(xiě)的學(xué)習。
準備工作:
打開(kāi)Delphi,選擇File\New\Others,選擇DLL Wizard自動(dòng)創(chuàng )建一個(gè)DLL工程,選擇File\New\Unit新建一個(gè)單元,全部保存。
寫(xiě)代碼:
在新建的Unit中定義一個(gè)抽象類(lèi):
Type
TCalculator=Class
Public
Function Addition (Op1, Op2: Double): Double ; virtual; abstract;
End;
這個(gè)類(lèi)很簡(jiǎn)單,就定義了一個(gè)抽象方法實(shí)現兩個(gè)數的相加。
定義抽象類(lèi)后,定義一個(gè)類(lèi)來(lái)實(shí)現這個(gè)抽象類(lèi):
Type
TCalcImple=Class (TCalculator)
Public
Function Addition (Op1, Op2: Double): Double; Override;
End;
方法實(shí)現:
Function TCalcImple.Addition(Op1, Op2: Double): Double;
Begin
Result: =Op1+Op2;
End;
當然我們還差一個(gè)創(chuàng )建這個(gè)類(lèi)的方法,我們在加入如下:
Function CreateCalcImple:TCalcImple;stdcall;
Begin
Result:=TCalcImple.Create;
End;
定義出口:
Exports CreateCalcImple;
這樣我們這個(gè)DLL的編寫(xiě)就完成了,我問(wèn)再來(lái)寫(xiě)一個(gè)Exe程序來(lái)調用它。具體在此就不再操作以便,一些列出Exe的源代碼:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
{定義與DLL中定義的抽象類(lèi)一樣的類(lèi),類(lèi)名稱(chēng)可以自定}
Type
TCalculator=Class
Public
Function Addition(Op1,Op2:Double):Double;virtual;abstract;
End;
type
TForm1 = class (TForm)
Button1: TButton;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
v_Obj:TCalculator;
end;
{靜態(tài)調用DLL,注意返回類(lèi)型與DLL中的不同,為抽象類(lèi)的名稱(chēng)必須與DLL中的一致}
Function CreateCalcImple:TCalculator;stdcall;External ‘ComDLL.dll‘;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
v_Obj:=CreateCalcImple;
Edit3.Text:=FloatToStr(v_Obj.Addition(StrToFloat(Edit1.Text),StrToFloat(Edit2.Text)));
v_Obj.Free;
end;
end.
升華到理論:
l D L L中定義的對象只能引出抽象方法。當建立對象時(shí), D L L向應用程序返回虛擬方法的指針表VTable。
l 在定義抽象類(lèi)的時(shí)候,定義的方法為:
Function Addition(Op1,Op2:Double):Double;virtual;abstract;
后面加上abstract(抽象方法表示)的原因是因為只有抽象方法才能被引出。
l 在執行文件中,通過(guò)DLL的出口實(shí)際只創(chuàng )建了一個(gè)接口,但是可以像使用對象一樣來(lái)調用其方法,這就有點(diǎn)開(kāi)始盡是于COM了。
有上面幾點(diǎn)引出:
1、COM的接口可以看成一個(gè)占位符,具體的實(shí)現是在接口對應的類(lèi)中;就像我們定義的抽象類(lèi)(TCalculator)中的Addition方法,只是一個(gè)沒(méi)有任何意義的描述符,但通過(guò)實(shí)現類(lèi)(TCalcImple)中引出后便有了具體的意義;
2、COM接口的訪(fǎng)問(wèn)必須通過(guò)其接口類(lèi)進(jìn)入后才能夠訪(fǎng)問(wèn)。就如我們例子中加入CreateCalcImple方法一樣,只有建立這個(gè)接口類(lèi)(Interface)后才可以訪(fǎng)問(wèn)具體的接口。
3、也就是說(shuō),一個(gè)COM必須具備三個(gè)方面的元素:接口定義類(lèi)、接口實(shí)現類(lèi)、接口創(chuàng )建類(lèi)。只要具備了這三個(gè)方面的元素就可以實(shí)現COM。
COM的接口(Interface)是COM的核心,所有的COM接口都是通過(guò)IUnknown派生出來(lái)的,它告知客戶(hù)那些接口是有效的,即已經(jīng)被實(shí)現類(lèi)說(shuō)定義。它定義的一般方式如下: ISimpleInterface=Interface(IUnknown) Function GetName:String Procedure SetName(v_Name:String) End; 如果在上面的接口中加入這樣一行: ISimpleInterface=Interface(IUnknown) V_Name:String; Function GetName:String Procedure SetName(v_Name:String) End; 這樣是不被允許的,因為上面我們說(shuō)到接口方法就像是一個(gè)占位符,需要實(shí)現類(lèi)引出才有實(shí)際意義,v_Name:String這一句只是一個(gè)數據成員將永遠無(wú)任何意義,如果要定義也只能在實(shí)現類(lèi)中定義。 現在舉一個(gè)COM的例子,沒(méi)有什么實(shí)際用處但至少說(shuō)明問(wèn)題: unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Label1: TLabel; Edit1: TEdit; Button1: TButton; Button2: TButton; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private { Private declarations } public { Public declarations } end; ISimpleInterface=Interface(IUnknown) Procedure SetValue(v_Value:Integer); Function GetValue:Integer; End; TSimpleImple=Class(TInterfacedObject,ISimpleInterface) Public Value:Integer; Procedure SetValue(v_Value:Integer); Function GetValue:Integer; End; var Form1: TForm1; v_Obj:TSimpleImple; implementation {$R *.dfm} { TSimpleImple } function TSimpleImple.GetValue: Integer; begin Result:=Value; end; procedure TSimpleImple.SetValue(v_Value: Integer); begin Value:=v_Value; end; procedure TForm1.FormCreate(Sender: TObject); begin v_Obj:=TSimpleImple.Create; end; procedure TForm1.Button1Click(Sender: TObject); begin v_Obj.SetValue(StrToInt(Edit1.Text)); Edit1.Clear; end; procedure TForm1.Button2Click(Sender: TObject); begin Edit1.Text:=IntToStr(v_Obj.GetValue); end; procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin v_Obj.Free; end; end. 藍色字樣即定義了一個(gè)接口,在形式上在ISimpleInterface(接口定義)和TSimpleImple(實(shí)現類(lèi))幾乎定義都差不多,但是我要強調的是,接口定義是為了實(shí)現OLE方式的訪(fǎng)問(wèn),而實(shí)現類(lèi)的定義,是接口功能的實(shí)現。兩者在功能和實(shí)現上都是有區別的。 COM對象的生存周期分為兩部分來(lái)講:客戶(hù)端與COM本身: 在客戶(hù)端,視定義的COM對象接口而定,像我們例子中的v_Obj,定義成全局變量,那么COM對象在創(chuàng )建時(shí)產(chǎn)生,只有在程序退出時(shí)才被釋放。我們也可以在形式上將其釋放,如:v_Obj:=nil,這樣這個(gè)COM接口就無(wú)效了。 在COM本身,COM接口的通過(guò)記數的方式來(lái)完成COM的生存周期,為什么采用記數,當然很簡(jiǎn)單——因為COM可能同時(shí)被多個(gè)程序所調用。有一個(gè)程序連接到COM時(shí)計數器加1,某個(gè)釋放時(shí)計數器減1,當計數器為0時(shí),COM對象才真正從內存中移除。 IUnknown接口: 為什么將IUnknown接口與生存周期放在一起講是有原因的,COM生存周期中的計數器就定義在IUnknown接口中:AddRef、Release、QueryInterface。這三個(gè)接口也是IUnknown的全部身家。對三個(gè)接口還是解釋一下: AddRef:當COM產(chǎn)生一個(gè)客戶(hù)端連接的時(shí)候,AddRef方法負責將計數器加1; Release:當COM釋放一個(gè)客戶(hù)端連接的時(shí)候,Release方法負責將計數器減1,如果計數器為0,釋放COM; QueryInterface:因COM支持多個(gè)接口,QueryInterface負責找出用戶(hù)指定的接口以返回正確的VTable; 接口全局標識: 上面說(shuō)到QueryInterface的時(shí)候,提到了要找到正確的接口。其實(shí)正確的接口就是靠全局標識符來(lái)識別的。它是一個(gè)128位的數字,是按照統計學(xué)的方法,計算出來(lái)的,可唯一標識出每個(gè)接口(理論上)。具體實(shí)現我們不用管,它產(chǎn)生的方法很簡(jiǎn)單,在Delphi中按Ctrl+Shift+G就可以產(chǎn)生一個(gè)。 相信通過(guò)上面的介紹對COM應該也有了初步的認識了,現在就將點(diǎn)實(shí)際的東西,如何在Delphi下編寫(xiě)COM。 在Delphi下面開(kāi)發(fā)COM是比較容易的,Delphi封裝的COM開(kāi)發(fā)的最基本的要素,只要你去編寫(xiě)對象的實(shí)現類(lèi)就行了,其他的全有Delphi搞定。 1、打開(kāi)Delphi,選擇File\New\Others,選擇Active頁(yè)的ActiveX Library,選擇File\New\Others,選擇Active頁(yè)的COM Object,出現的向導中比較重要的選項如下: Class Name:實(shí)現類(lèi)的名稱(chēng),自定。 Include Type Library:是否包含類(lèi)型庫文件,如果不選擇,Delphi將不產(chǎn)生類(lèi)型庫文件,應此上面輸入的Class Name也無(wú)效。也就意味著(zhù)接口類(lèi)、實(shí)現類(lèi)、實(shí)現方法都的自己寫(xiě)。對于不是很熟悉COM的的人員最好不要采用這種方式。 其它參數均可采用默認值,具體意義可參見(jiàn)有關(guān)資料。 2、接口的編寫(xiě) 選擇View\Type Library,選擇接口,右鍵New選擇Method,在右邊Attributes的Name中輸入接口的名稱(chēng),在Parameters中加入需要加入的輸入和輸出參數。注意:設置參數類(lèi)型時(shí),如果是返回參數的,參數類(lèi)型后面要加上“*”。點(diǎn)擊刷新,在程序單元中就出現了剛定義的接口,在此編寫(xiě)實(shí)現代碼就可以了。 3、COM的安裝 編寫(xiě)完成后編譯,通過(guò)Run\Register ActiveX Server注冊編譯好的COM,通過(guò)Run\Install COM+ Objects安裝COM組件,在彈出的對話(huà)框中選擇接口,在接下來(lái)的對話(huà)框中可以選擇安裝到已有的COM應用程序中也可以安裝到新的COM應用程序中。這樣就完成了COM的安裝,你可以打開(kāi)系統中的組件服務(wù)看到你所安裝的COM。COM的理論
以實(shí)例來(lái)講
COM對象的生存周期與IUnknown接口
COM實(shí)現在Delphi中的實(shí)現
聯(lián)系客服