(轉錄自 凝視、散記 )
COM是Microsoft物件導向架構的基礎,本文主要是重點介紹COM的理論及運作機制。基礎理論包括:COM的概念、應用及定義。運作機制探討COM底層的運作,將提到:COM的介面、識別及呼叫,更進一步探討跨行程及跨機器時如何呼叫。最後很簡要的介紹COM以外另外CORBA及JAVA的做法。
什麼是元件(Component)?
元件是一種有公開的屬性、方法、事件,可以在應用程式中呼叫的二進位檔案。主要的目的是為了在程式開發時可以「重複使用」這些已經編譯好的檔案,以提高開發效率降低成本。此外,因為元件的發行與使用都是二進位檔案,所以在安裝及使用時都是看不到原始碼的,也可以達到保護source code的目的。再就Internet的應用而言,由於元件已經經過編譯,所以在執行的速度上會比ASP指令檔(Script)來得快,因此較佳的執行效能也是使用元件的原因之一。
如果我們將應用程式想成是一件產品;元件就好像是產品的零件。當你完成產品的需求規劃後,不需要自行撰寫所有的程式,只要選取或購買符合需求的元件進行組裝便能完成專案。這種系統開發的哲學就是所謂的CBSE—Component Based System Engineering。CBSE發展應用程式的流程如下:定義應用程式的功能à設計使用者介面à查詢軟體元件目錄尋找符合功能的軟體元件à查詢各軟體元件的介面(interface)à撰寫應用程式組合各軟體元件à輸入各軟體元件的介面值à呼叫各軟體元件à完成系統功能。
什麼是COM (Component Object Model)
每個元件都可視為是一段獨立的副程式,讓應用程式透過Run Time時與元件的相互連結而完成系統功能,便是Microsoft發展COM主要的目的。Microsoft COM的架構也是CBSE的實現,程式開發者只要有軟體元件的清單及清楚定義的介面,便可在應用程式中重複使用這些現成的元件。
1991年Microsoft提出一項新的規格 OLE (Object Linking and Embedding) 。Microsoft當時的OLE 1.0,只提供處理「複合文件」﹙compound document﹚的功能,這種以文件為中心﹙非以應用程式為中心﹚,能夠在單一文件儲存如文字、圖形、視訊與聲音等多重格式資料的規格並未被廣泛接受。到了1993年Microsoft公佈OLE 2.0版的規格,就是COM 架構,包含更多功能,最重要的,COM規範了程式間互動﹙interoperability﹚的方法與介面,融入了封裝、多型等物件導向的觀念。
其實Microsoft大部份的產品都已COM化。如:IE、OutLook、Excel等不僅都是可以獨立使用的軟體,也都是component。只要知道它們提供了哪些 API﹙Application Program Interface﹚,你便可以在程式中把它們當成元件來呼叫。你要做的只是翻翻programmer guide,查出所提供的 method,然後就可以在程式中呼叫它們來完成工作。 而且你寫的軟體也可以成為別人軟體的元件或一項功能(feature) 。在使用元件之前,必須執行「服務註冊」的指令﹙在Dos模式之下執行Regsvr32 myCom.dll﹚,向作業系統「註冊」該元件。然後就可以在有支援 COM/DCOM的開發工具,如:VB, Delphi, Visual FoxPro, C++ Builder, Active Server Page (ASP)中來使用已註冊的元件。
COM的定義
COM是一套規範,規定了製作具有動態交換能力元件的方法。它制定了用戶端程式與及元件間溝通的介面,以達到相互操作﹙Interoperability﹚所應共同遵循的一套標準。讓二進位物件程式的設計、使用以及獨立開發成為可行。事實上,COM並不只是一份規格,它還實作了一組API,叫做COM程式庫,提供所有用戶端程式與元件都會用到的元件管理服務。
COM元件包含以Win32動態連結程式庫( DLL )及以 .EXE形式存在的可執行檔,遵循COM的標準所撰寫出來的元件具備以下幾點的特性:
- 可以用二進位的格式傳送。
- 具有語言獨立性:可以使用任何語言撰寫,也可以呼叫任何語言的元件。
- 是動態連結的形式。
- 具位置透通性﹙location transparent﹚:用戶端程式可以將遠端機器上的元件當成本地端機器上的元件一樣使用。
- 與用戶端程式獨立:可以在不干擾舊有用戶端程式的前提下進行新物件的改版。
COM的運作機制
每個COM物件都提供一個以上的介面( Interface ),每個介面都提供一些方法。client端只能透過其介面呼叫COM物件介面內的方法,來使用該物件的服務,而無法直接存取物件內的資料。
COM的介面指的是記憶體中一個包含函式指標陣列的Virtual function table,每一個陣列元素儲存指向元件所實作的函式位址的指標。COM所用來定義介面的標準為IDL—介面定義語言﹙Interface Definition Language﹚。IDL是Open Source Foundation為了在分散式計算環境中使用RPC﹙Remote Procedure Call﹚所制定的。
COM規定所有的介面都必須繼承IUnknown介面﹙註﹚,因此每個COM介面都一定包含:QueryInterface、AddRef與Release等三個函式。
每個COM Object都是屬於特定類別( class )的個體( instance )。我們必須先經由COM程式庫得知道物件的類別,才能開始執行該物件的真正個體。因為client端在使用COM物件介面的方法前,必須先取得該介面的指標﹙Pointer﹚。因此當client端尚未取得此物件所提供的任何介面指標時,會利用COM程式庫中的CoCreateInstance函式﹙註﹚來取得物件的第一個介面指標。當server便開始執行此類別的實作後,client端便可以直接向該物件要求其它所提供的介面的指標,進而使用該物件的任何方法或服務。
每個物件會利用CLSID作為鍵值在Windows的Registry登錄資料庫中記錄元件所在的DLL檔案名稱,以便讓CoCreateInstance利用CLSID作機碼查得檔案的名稱。 Registry是Windows作業系統用來紀錄系統軟、硬體、設定、以及使用者的相關資訊,必要時可以在Registry中新增紀錄或讀取資料。COM的用戶端程式就是利用Registry找出所要使用的元件。
Client端在建立物件透過CoCreateInstance取得該物件第一個介面指標後,就可以呼叫QueryInterface,並傳入該介面的IID,利用IUnknown::QueryInterface,向物件要求取得其他方法所在介面的指標。如果成功的話,就可以使用傳回的指標,便得知物件所提供某個特定介面。
介面一旦公佈就不能再更改了。如果必須異動,只能再定義一個新的介面,新的介面可以繼承原來舊的介面,不過介面的唯一識別碼IID,就是Globally Unique Identifier ( GUID )一定是唯一的,不可以重複。
GUID ( Globally Unique Identifier )是為了讓全球程式設計師所設計的COM物件及介面不必依賴集中管理就能擁有唯一的識別。我們可以藉助Microsoft的提供的工具程式GUIDGEN.EXE或UUIDGEN.EXE來產生唯一的GUID。COM介面的GUID可由IID這個特別資料型別來表示。而Class也有自己的GUID,為了與IID區別,COM另外定義了CLSID這個資料型別,專門用來放置類別的識別碼。
使用COM物件時,客戶端會強制該物件的新個體開始執行。該如何管理物件的執行?而且通常同時會有許多client端程式使用同一物件,物件應該在何時停止執行?
這個問題由IUnknown介面所提供的AddRef以及Release所maintain的 Reference Counter而獲得解決。每個COM物件都有自己的Reference Counter,每當物件傳遞出一個介面的指標時,Reference Counter便會加一。每結束一個使用介面時,必須透過介面指標呼叫Release, Reference Counter便會減一。當物件的Reference Counter等於0的時候,物件本身便會destroy。
COM的類型
每個COM物件的實作部分都位於伺服器( Server )中。Server中含有真正用來實作物件方法的程式碼,並且負責維護物件的資料。COM有三種類型:
- In-process Server:物件的實作係位於動態連結函式庫中,執行時與client端為於同一process中
- Local Server:物件的實作位於與客戶端相同的機器中,但是在分開的行程內。
- Remote Server:物件的實作位於與客戶端不同的機器內所執行的DLL或獨立行程中。跨機器的分散式系統必須借助Distributed COM ( DCOM ) ﹙註﹚的支援才得以完成。DCOM是一個高階的網路協定,建立於COM之上的規格和服務,讓位於不同電腦上的行程內之COM元件可以互相運作
對client端而言,不管物件實作是在哪一種伺服器中,都是透過介面指標,來啟動物件、取得物件介面的指標、叫用方法、釋放指標,沒有任何差別。不過server上的機制便有所不同。
第一種情形:In-process Server:COM物件的實作位於行程中的伺服器﹙如圖一﹚。
client端的介面指標直接指向物件的介面。透過vtable裡的函數指標直接呼叫物件的方法。
第二種情形:Local Server:COM物件的實作位於本地端的不同行程中﹙如圖二﹚。
由於vtable裡的函數指標無法跨行程,因此client端的介面指標無法直接指到物件的介面,因此會改為指向client端行程內的proxy介面,由proxy介面將存取要求透過RPC轉送給stub。client端將proxy介面是為object而真正的object則將stub視為client。
第三種情形:Remote Server:COM物件的實作是位於不同機器上﹙如圖三﹚。
在DCOM中所加入支援遠端伺服器的架構,client、proxy及stub間的互動與Local Server類似。
無論是跨行程或是跨機器的情形,在伺服器與client端中,都需要傳輸參數與資料的標準格式,以及進行溝通。proxy將呼叫的參數包裝為標準格式的動作稱為marshaling,透過RPC傳輸給stub,stub將收的到要求,卸除改為接收行程適用格式的相反動作,則稱為unmarshaling。Proxy與Stub的存在主要就是為了進行marshaling與unmarshaling。
Proxy與Stub的建立只要使用IDL ( Interface Description Language )來撰寫介面,就可以讓MIDL ( Microsoft IDL )編輯器幫我們產生proxy與stub DLL的程式,不需要自己動手撰寫這些程式。
元件開發的不同架構
在Microsoft的架構之下你可以利用VB及VC來開發元件.利用VB的ActiveX DLL來開發元件是最快速的方式。而MTS提供元件的管理以及交易(Transaction)的管理.這種架構在Windows 2000上已經整合為COM+。這也是Microsoft分散式應用系統設計的開發架構。
目前在元件的開發方面發展,有三大陣營 -
DCOM (Distributed Component Object Model)、CORBA (Common Object Request Broker Architecture ) 和 Java RMI (Remote Method Invocation)
Microsoft的 DCOM 就是根據其原本非分散式物件模組 COM 所進化而來的,它已被定為新一代電腦語言的發展基礎。DCOM 其實是二次元層次 (Binary Level) 的分散式物件介面標準。它既可支援 NT 及 Windows 98,此元件更可伸延到Internet之策略,Active X 就是其中的表表者。
CORBA 是由七百多間業內的公司參與之 OMG (Object Management Group) 所制定的標準,其目的是要整合大部份的物件系統。這種物件管理架構 (OMA,Object Management Architecture) 的主要溝通機制,其中物件與物件間之溝通是透過物件訊息仲裁者 (ORB)。而 ORB 是在主從架構間提供一仲裁服務,決定訊息的流向。CORBA 規範介於物件介面的層次 (Interface Level),而物件間就以介面描述語言 (IDL, Interface Definition Language) 來描述。將介面 (Interface) 與實作 (Implementation) 分開,提供多重的繼承架構及動-靜態介面的召喚方式。
Java 是昇陽微系統 (Sun Microsystems) 所提出結合了程式語言和虛擬機器 (Virtual Machine) 的分散式物件環境。與 DCOM 和 CORBA 比較,它不能採用多種不同的程式語言來發展,不過由於其虛擬機器的環境,使 Java 容易地跨平臺運行。