最近重新研究
元件物件模型(Component Object Model, COM),再次閱讀MSDN上的文件,並且在MSDN上面加上解釋 ,不過貼了一陣子之後卻被取消張貼,我想可能是我在英文版MSDN貼上中文的關係吧!現在趕緊將內容編寫在自己的部落格中記錄下來,避免閱讀心得遺失。
什麼是Component Object Model (簡稱COM)?中文微軟翻譯成「元件物件模型」,
COM是一個標準(standard),定義元件要長什麼樣子,以及元件跟元件之間要如何互動。COM真的很重要!COM是OLE與ActiveX兩個的技術基礎。除了MSDN的資料之外,有關COM的資料相當稀有,參考書籍大概只有Dale Rogerson著的「Inside COM」,中文書名是「完全剖析COM」(黃昕暐編譯,徐銘志校閱),不過都已經絕版,以後大概沒有人會寫COM了!
http://www.microsoft.com/mspress/taiwan/Pages/c0089_1061.htm
有關COM的中文資料相當少,書籍更少,只能參考MSDN的說明。COM是一個「標準(Standard)」,規範二進制機器碼的元件要如何實現,不要把COM想得很困難,唯一需要做的就是花時間研讀這些文件,建議先從「COM Fundamentals/Guide/The Component Object Model」開始閱讀。
在Visual Studio中開發COM的話,則是使用「ATL專案」類型。
一般來說,軟體的物件(想像成一個資料結構,這裡不是特別指OOP的物件)會包含資料(data)與函式(function)兩個部分。COM規範存取資料只能透過函式,所以說COM元件都是一堆函式。
COM標準將一組函式稱為「介面(Interface)」,介面裡的函式COM稱之為「方法(method)」,注意這裡的介面與方法和物件導向程式的定義不相同,觀念不要混淆了。
這文件寫那麼多,重點只有一個觀念,介面(interface)與介面實作(interface implementation)是不相關的,兩者是獨立的兩件事情。COM標準只有定義介面,介面是一堆方法(method),也就是函式原型(function prototype),但
COM標準沒有規定方法要如何實做,實作部分留給程式設計師去做。介面定義是一種協議(contract),因此各個物件與應用程式就知道如何去使用COM元件,這就是COM的精神。注意介面實作不一定要實現,但是介面一定要存在於元件中,換句話說,函式中的程式碼可以是空的,不做任何事情。
COM元件中的資料處理只能透過「介面(interface)」存取,COM所謂的介面指的是一組預先定義的函式原型,另一個角度來說,COM的標準就是只有定義介面(不只一個,參閱COM的Reference)。而所謂「實作(implement)」則是表示實做介面,撰寫介面所定義函式的程式碼。
我們已經知道COM是個標準,COM是定義一群介面。那要如何實現介面?介面的實作是用:指向函式表(function table)的指標,稱為
介面指標(interface pointer),函式表是一個函式指標的陣列,陣列中的函式指標都是介面所定義的方法。注意,
每個介面都有一個識別碼,COM稱為IID(unique interface identifier),是一種GUID(globally unique identifier )的資料類型,我們因此可以透過IID取得COM元件的介面。
IUnknown介面是COM標準中最重要的一個介面,所有介面都繼承這個IUnknown介面,換句話說,COM 元件一定具有IUnknown介面,我們一定可以呼叫這三個方法:QueryInterface、AddRef與Release。
使用COM標準實做出來的是什麼?答案是動態連結程式庫(DLL)或可執行檔(EXE),因為COM是「二進制的標準(binary standard)」,直接定義最底層執行碼的標準。注意,
COM不是要與DLL競爭或取代,而是將DLL運用地更好的一種方式,使用DLL可以解決的,若是換成使用COM的方式會處理地更好。
Visual C++在COM介面的實作是:宣告一個類別當作COM的介面(interface)的實作,在這個「介面的類別」中宣告虛擬函式(virtual function)當作COM方法(method)的實作。
COM這個標準討論的都是介面(interface),都是介面啊!介面!介面!一定要了解什麼是介面。這些介面當中,又以IUnknown介面最為重要,而且IUnknown介面的QueryInterface方法幾乎定義了整個COM元件。
這裡的重點是「
介面繼承(Interface Inheritance)」的觀念,COM的繼承觀念不同於物件導向程式設計的繼承。
介面繼承是指重複使用「函式原型」(COM中稱為方法),不是程式碼的重複使用,在COM的標準下最重要的介面是IUnknown介面,每個COM標準下的介面都會繼承IUnknown介面。IUnknown介面定義3個重要的方法,分別是QueryInterface、AddRef與Release方法,其中QueryInterface是用來取得COM元件中其他的介面,而AddRef與Release則是用來管理COM元件的生命週期。
COM標準除了定義介面和互動之外,COM也有提供一個
程式庫(library),程式庫提供操作COM元件的常用動作給開發者使用,這裡的function就是COM程式庫提供的功能,動態連結的程式庫位於C:\Windows\System32\Ole32.dll之下,靜態連結的程式庫則位於C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\Ole32.Lib,標頭檔是C:\Program Files\Microsoft SDKs\Windows\v7.1\Include\ObjBase.h,路徑是對於Microsoft Windows SDK for Windows 7 and .NET Framework 4而言。
登錄資料庫(Registry)是Windows記錄有關軟硬體與使用者的資訊,應用程式可以從Registry加入或讀取資訊。在Registry中會包含所有已經安裝在系統的COM元件資訊,應用程式利用CLSID或ProgID的機碼取得DLL或EXE所在的路徑。
COM標準是規範元件的介面與互動方式。使用COM標準做出來的COM物件,在Windows中是利用CLSID機碼識別各物件,一個物件會有多個介面,而介面的識別則是利用IID機碼。
相當於Dll中DllMain函式的用途。當呼叫CoCreateInstance或CoGetClassObject建立物件時,透過COM程式庫會呼叫DllGetClassObject,概念像是COM元件的Entry Point。細部動作則是透過IClassFactory介面的CreateInstance方法建立物件。
呼叫CoCreateInstance函式的動作是:由COM程式庫去呼叫DLL中的DllGetClassObject函式。
呼叫CoFreeUnusedLibraries函式的動作是:由COM程式庫去呼叫DLL中的DllCanUnloadNow函式。
COM物件再利用的方法採用:包含(containment/delegation)與聚合(aggregation)兩種方式。
COM的安全性是基於Windows與底層RPC的安全機制,透過驗證(authentication)與授權(authorization)方式取得,驗證是判斷呼叫者的身分,而授權是指判斷呼叫者是否可以去執行某個函式。在COM標準中有兩種安全性形式:活動安全性(activation security)與呼叫安全性(call security),活動安全性是指客戶端是否可以進入伺服端,而呼叫安全性則指進入伺服端之後,判斷是否可以存取伺服端的物件。
「COM is still valuable」對,同意COM依舊有價值。COM元件是本身就是二進制的執行碼,所以執行效能很高,微軟目前以.NET Framework為主要開發平台,使用Assembly組件,.NET Framework不須管控記憶體,加上.NET Framework的元件多,進而提高生產力,代價就是效能差了一點!兩者的優劣在開發使用上必須取捨(tradeoff)。學習COM,同樣需要去了解ATL,COM與ATL就像「雞生蛋,蛋生雞」的關聯性,建議先從COM切入,再去了解ATL,反覆咀嚼才可破。
使用COM程式庫之前,必須呼叫CoInitialize函式進行初始化。使用完成,必須呼叫CoUninitialize函式關閉COM程式庫以釋放使用的DLL。使用記得#include "objbase.h"(如果是用Visual Studio可以不需要),接著呼叫「::CoInitialize(NULL);」就可以了。
使用元件的先決條件:(參考「完全剖析COM」一書)
- 元件必須是以動態連結的方式加入應用程式
- 元件必須隱藏(或封裝)實作的細節。
元件必須符合的限制:(參考「完全剖析COM」一書)
- 元件必須隱藏實作時所使用的程式語言
- 元件必須以二進位格式存在
- 元件的升級不能造成目前使用者的困擾
- 元件必須具有網路通透性。
要把COM搞懂,概念與實作都不困難,但是需要花時間閱讀文件。
###