作者:巴哈姆特
http://www.cnpack.org
(轉載請注明出處并保持完整)
上一篇,我們介紹了接口。如果沒有接觸過COM對象的話,你會覺得接口真的很麻煩,也許會有:“還不如直接定義一個類更方便”的想法。
????的確,沒有經過COM封裝的接口確實比較麻煩。在我看來,沒有經過COM封裝的接口似乎沒有存在的意義。那么,什么是COM對象呢?它有什么優點呢?接下來開始對COM對象進行一個簡單的介紹:
????COM是個二進制規范,它與實現的語言無關。這樣,即使COM對象由不同的編程語言創建,運行在不同的進程空間和不同的操作系統平臺,這些對象也能相互通信。COM既是規范,也是實現,它以COM庫的形式提供了訪問COM對象核心功能的標準接口以及一組API函數,這些API函數用于創建和管理COM對象。COM本質上仍然是客戶服務器模式。客戶(通常是應用程序)請求創建COM對象并通過COM對象的接口操縱COM對象。服務器根據客戶的請求創建并管理COM對象。當然,客戶和服務器這兩種角色并不是絕對的。
????記得我在剛剛接觸COM對象的時候,我師傅曾經給我說過:“COM不是Dll,雖然它可能會以后綴名為dll文件呈現在你面前,但是它絕對不是我們傳統意義上所說的dll(動態鏈接庫)”。
????其實在我看來,進程內的COM對象應該是一個以dll為載體而提供一些特殊服務的特殊的動態鏈接庫。當然,也有進程外的COM。
????現在,我們演示怎么在Delphi中利用向導建立一個簡單的COM模型。
????首先:打開Borland Delphi 7.1(不好意思,我一般都是用這個版本。什么?為什么是7.1?就是7.0加個Update1補丁包嘛-_-||)。
????然后:把Delphi默認為我們創建的Application關掉,并在菜單中選擇File->New->Other,之后在彈出的窗口中找到ActiveX頁,雙擊ActiveX Library圖標。
????雙擊圖標后,我們可以看到Delphi已經幫我們建立了一個ActiveX庫,代碼如下:
library ?Project1; uses ?? ComServ; exports ?? DllGetClassObject,??? // 返回類工廠的接口 ?? DllCanUnloadNow,????? // 是否可以釋放該組件 ?? DllRegisterServer,??? // 注冊函數 ?? DllUnregisterServer;? // 反注冊函數 {$R *.RES} begin end . |
我們會看到,在工程中,Delphi已經幫我們定義好了四個輸出函數(關于這幾個函數更詳細的說明,可以查閱更多的資料),我們先不管它們。
????接下來,我們再使用菜單File->New->Other并在ActiveX頁中創建Com Object,這時,我們可以看到一個對話框:其中ClassName是我們的對象名、Instancing是對象創建模式、Threading Module為線程模式。我們使用NewComServer作為對象名,其他默認。OK后可以看到一個標題為“Project11.tlb”的窗口,這個我們可以在這個窗口中為接口添加新的方法,例如我們添加一個GetMessage方法。然后我們打開Unit1.pas可以看到如下代碼:
unit ?Unit1; {$WARN SYMBOL_PLATFORM OFF} interface uses ?? Windows, ActiveX, Classes, ComObj, Project1_TLB, StdVcl; ???? // Project1_TLB 接口所在單元 type ?? TNewComServer = class (TTypedComObject, INewComServer) ???? // 實現接口的類 ?? protected ???? function ?GetMessage: HResult; stdcall; ?????? // 我們剛剛添加的方法 ???? {Declare INewComServer methods here} ?? end ; implementation uses ?ComServ; function ?TNewComServer . GetMessage: HResult; begin ?? MessageBox( 0 , \'測試\', \'提示\', $40 ); ?? Result:= GetLastError; // 我添加的代碼 end ; initialization ?? TTypedComObjectFactory . Create(ComServer, TNewComServer, Class_NewComServer, ciMultiInstance, tmApartment); ???? // 類工廠 end . |
?
?
之后,我們編譯這個工程(CTRL+F9)將會生成一個Project1.dll文件。保存并關閉這個工程。
??? 接著,我們編寫一段代碼來測試這個COM工程:創建一個普通的應用程序工程,并引用Project1_tlb單元:
var ?// 注意,在測試代碼中也需要引用project1_tlb單元,由于我們的接口聲明在該單元內 ?? NewComObject: INewComObject; // 聲明接口 begin ?? NewComObject:= CreateComObject(CLASS_NewComObject) as ?INewComObject; // 創建COM對象, ???? //CLASS_NewComObject 的定義可以在Project1_tlb.pas里找到 ?? if ?NewComObject <> nil ?then ???? begin ?????? NewComObject . GetMessageInfo; // 調用接口中的方法 ?????? NewComObject:= nil ; // 釋放接口 ???? end ?? else ???? ShowMessage(\'對象創建不成功\'); end ; |
?
注意我們在運行這個EXE之前,需要先把我們之前的COM工程注冊給系統:開始->運行->regsvr32.exe \"...Project1.dll\"。看到注冊成功的提示信息后,我們現在可以運行我們剛剛編寫的那個測試程序來測試我們的COM對象了,看看執行測試代碼后,是否彈出了一個標題為“提示”,內容為“測試”的對話框呢?
??? 我們可以看到,在COM組件創建好以后,在EXE調用的時候是相當簡單的,而且,當我們某個方法的實現細節發生改動,只要方法聲明不變,那么在軟件升級的時候,我們可以只升級我們需要升級的COM組件,而不需要改動其它的地方。這樣可以有效的減輕維護的工作量。
??? 當然,這個演示只是一個進程內的COM,至于更詳細的說明,可以去參閱更多的資料。
??? 友情提示:ActiveX是Windows下實現COM的一個組件規范。請不要把ActiveX和COM之間劃上等號!