概要
當編寫好一個COM並將其在系統中注冊之后,這些注冊表項到底位於Register中的什么位置,系統是如何通過這些注冊表項在Runtime時候找到某個COM的,這些都是診斷COM相關的問題至關重要的信息。總的來說,系統是通過GUID來查找每個對象的,比如TypeLib,Interface和Class都有其相應的GUID(16bytes的字符串)。本文介紹了一個COM在注冊后,系統注冊表會被寫入哪些鍵值,以及這些鍵值所代表的意義。
正文
COM是一種基於獨立於開發語言的編程模型。因此在描述COM功能和接口的時候采用了與編程語言無關的接口描述語言(IDL)來編寫接口的抽象定義,該接口定義文件(*.IDL)經過Platform SDK自帶的Microsoft IDL編譯器編譯之后將生成相應的頭文件、類型庫等。
如下是一個簡單的類型庫定義文件(OOPCOM.idl)
// OOPCOM.idl : IDL source for OOPCOM.dll // This file will be processed by the MIDL tool to // produce the type library (OOPCOM.tlb) and marshalling code. import "oaidl.idl"; import "ocidl.idl"; [ object, uuid(DB20D9BF-FAA4-4D23-9243-19860EB4482A), dual, helpstring("ISimpleClass Interface"), pointer_default(unique) ] interface ISimpleClass : IDispatch { [id(1), helpstring("method HelloWorld")] HRESULT HelloWorld([out,retval]BSTR *result); }; [ uuid(FE0FAA57-2E27-425F-9FA3-518E73F729FB), version(1.0), helpstring("OOPCOM 1.0 Type Library") ] library OOPCOMLib { importlib("stdole32.tlb"); importlib("stdole2.tlb"); [ uuid(1CF4A019-A754-44F1-B164-047A3F0AC184), helpstring("SimpleClass Class") ] coclass SimpleClass { [default] interface ISimpleClass; }; };
本文主要介紹以下幾個COM相關常見概念:
- COM Interface
- COM Class
- Type Library
- Application ID &Name
COM的相關表項在注冊表中的位置及其意義:
表項一:COM Interface
注冊表路徑 : HKEY_CLASSES_ROOT\Interface\[X] 或者:HKLM\Software\Classes\Interface\[x]
在以上的OOPCOM.idl,我們看到其Inferface ID (IID)為DB20D9BF-FAA4-4D23-9243-19860EB4482A,其在Register中的位置如圖所示。
在COM的Interface表項中包含
- ProxyStubClsid :該鍵值的具體內容請參考下圖。
- ProxyStubClsid32 :該鍵值的具體內容參考下圖。該鍵值與ProxyStubClsid相同值。
- TypeLib: 與該interface相關的COM 類型庫定義。
在Interface中,有關ProxyStubClsid和ProxyStubClsid32所指向的CLSID值如下圖所示:
ProxyStubClsid鍵值和ProxyStubClsid32起什么作用呢?這需要對COM client和COM service是如何進行通訊和數據交換的有所了解。
當COM client和COM Server application不在同一個進程地址空間時,COM會建立一個客戶端的proxy和server端的stub。例如當client調用CoCreateInstance()時,在服務器將接口指針返回給COM之后,COM將接口指針返回給client之前,COM為指定的接口創建proxy和stub並將指向proxy的指針返回給client。我們把類似這樣的過程叫做Marshaling (匯集)。
開發人員可以實現IMarshal這個interface來支持自定義的Marshaling。而如果對象不支持自定義Marshaling,則COM使用proxy和stub程序執行標准Marshaling,因此COM必須從注冊表中獲得提供proxy和stub程序生成器的DLL的CLSID。這就是我們為什么ProxyStubClsid(32)的意義所在。
如果開發的COM支持自動化技術,即實現了IDispatch interface(支持解釋型語言的調用,如vbscript等),則其Marshaling的DLL一般為以上所示的oleaut32.dll (16bit系統則為ole2disp.dll)。如果其不支持該技術,則其Marshaling的DLL一般為ole32.dll
表項二:COM Class
注冊表路徑: HKEY_CLASSES_ROOT\CLSID\[X] 或者:HKLM\Software\Classes\CLSID\[x]
缺省情況下,在CLSID下面包含以下鍵值(如上圖所示)
- LocalServer32
- ProgID
- TypeLib
- Programmable (omitted)
LocalServer32:
該鍵用來指定運行在進程外的COM Server Application的.EXE文件path。與之相似的一個表項是:InprocServer32。COM運行模式有2種,一種為進程內模式,即COM與Client在同一個進程內存之內,運行在這種模式下的COM必須只能為*.DLL,指定其具體COM module的path的時候,即用InprocServer32來指定;另外一種是Out of Process(OOP),即Client和COM Server Application位於不同的Process地址空間,而這種COM必須為*.EXE,指定其具體COM module 的path時候,即用LocalServer32來指定。
而這兩種模式下的COM調用過程也不盡相同。
1.服務器為*.DLL時,調用過程如下所示:
2. 當服務器為*.EXE 時,其過程如下所示:
ProgID:
實際上ProgID和CLSID都是一回事,只不過是2種不同的叫法而已,ProgID可以說是CLSID的nickname吧。相對於CLSID一串16bytes的難以記憶的數字, ProgID顯得更為人性化,系統提供了相應API: GetClsIdFromProgid(t_ProgID) 可以實現ProgID和CLSID之間的互換。
TypeLib:
不用多講了, 該鍵在COM interface鍵中也出現過,用來指定該COM的類型庫文件。
既然在COM Interface和COM class中都出現了Type Library,那就看看TypeLib 鍵吧。
表項三: COM Type Library
注冊表路徑: HKEY_CLASSES_ROOT\TypeLib\ [X]
TypeLib的各項鍵值如上圖所示。
還有一項不太注意的Register Entry就是AppID。當把COM注冊到系統之后,系統會在 HK_ROOT\AppID 下面生成一個指向該COM的AppID Entry。缺省情況下,會使用COM module name作為該Application name,從而使得該COM/DCOM 可讀。如下圖5在dcomcnfg.exe中所示,該AppID和一個readable的Application Name對應起來。
表項四:COM Application相關注冊表項
注冊表路徑: HKEY_CLASSES_ROOT\AppID\[X]
希望以上內容對您有所幫助
Winston He