2009-10-2
======================
《深入解析MFC》筆記 11. MFC 實現 COM
======================
COM ( Component Object Model,組件對象模型)
OLE 是微軟的對象技術的名字,是一種整合軟件的統一方法,使得軟件可以隨時間而演變,是一種整合技術。
從時間的角度講,OLE是最早出現的,然后是COM和 ActiveX;
從體系結構角度講,OLE和ActiveX是建立在COM之上的,所以COM是基礎;
OLE的主要目標是使得應用程序被外界所認識和理解。
使用COM編程的目的是使得軟件從源代碼以及的重用上升到二進制一級的重用。
組件是一個可重用的模塊,由一組處理過程、數據封裝和用戶接口組成的業務對象(Rules Object)。組件看起來像對象。它們的主要區別是:
1)組件可以在另一個稱為容器(有時也稱為承載者或宿主)的應用程序中使用,也可以作為獨立過程使用;
2)組件可以由一個類構成,也可以由多個類組成,或者是一個完整的應用程序;
3)組件為模塊重用,而對象為代碼重用。
組件模型有COM(Component Object Model,對象組件模型)/DCOM(Distributed COM,分布式對象組件模型)和CORBA(Common Object Request Broker Architecture,公共對象請求代理體系結構)。
=================
COM
------------------------
COM 類
COM 所做的全部工作就是在二進制一級共享軟件。
C++ 類 和 COM 類
1、C++使用操作符new來實例化一個對象,而COM對象通過API的函數來創建。
2、C++使用delete來刪除一個對象,COM使用引用計數來控制對象的生存期,引用計數為0,自動刪除。
3、C++使用特殊的機制來控制運行時類型信息(run-time type info)和類型轉化(typecasting),而COM類通過成員函數來支持
4、C++類提供成員函數和數據以供外部訪問,COM中,類只提供成員函數。
5、COM類沒有具體規定的布局。
6、COM引入了一套接口的規范來形式化對象指針的概念。
使用COM對象:
①、給操作系統發送實例化一個COM的請求;
②、得到對象的一個接口指針
③、使用接口; ④、釋放接口
------------------------
COM 接口
一個接口就是一組等待被實現的純虛函數
GUID
Globally Unique Identifier 128位數字
COM 類的ID用 "CLSID"作為前綴,
接口 ID 用"IID"作為前綴
=================
IUnknown接口
struct IUnknown{
virtual HRESULT QueryInterface ( IID& iid, void** ppvObj ) = 0;
virtual ULONG AddRef() = 0;
virtual ULONG Release() = 0;
}
-------
對象生存期管理
COM對象自己控制生存期。
COM 對象的引用計數由 IUnknown 接口的 AddRef() 和Release() 這兩個函數來管理。
--------
接口協商(negotiation,運行時發現功能)
當一個客戶擁有了一個COM對象的一個接口指針,就能夠通過 QueryInterface() 發現這個對象所支持的別的接口
HRESULT QueryInterface ( REFIID riid, LPVOID far* ppvObj );
riid用來標識所要得到的接口的 GUID。
ppvObj 用來存放接口指針
實現 IUnknown::QueryInterface的一個方法:
HRESULT CoSomeObject::QueryInterface ( IID& riid, void FAR** ppvObj ){
HRESULT hr = ResultFromSCode ( E_NOINTERFACE );
*ppvObj = NULL;
if ( riid == IID_IUnknown ){
*ppvObj = ( IPersist * ) this;
hr = NOERROR;
} else if (riid == IID_IPersist ) {
*ppvObj = ( IPersist * ) this;
hr = NOERROR;
}
return hr;
}
-------------
調用 、 使用 、釋放。
1、調用某個方法得到一個接口(通過初始化一個對象或者通過一個存在的接口調用 QueryInterface())。
2、使用接口內的函數。
3、調用Release() 對對象引用計數減1.
========================
COM 對象服務器
COM類存在于服務器之中。COM服務器分為兩種: 進程內服務器(in-proc)和進程外服務器(out-proc)。
in-proc server 是 DLL形式的,他們與客戶在同一個進程空間內。
out-proc server 是作為 EXE文件形式存在,和客戶在不同的進程空間內(甚至在不同機器).
進程內服務器(DLL)
進程外服務器(EXE)
有更強的魯棒性,進程外服務器需要付出額外的代價,列集(marshaling)。
一個進程空間內的地址(數據地址、函數指針)在另一個進程空間內不起作用,需要用遠程過程調用(RPC)和列集(marshaling)。
marshaling:
每一個接口,在客戶代碼中有一個代理(proxy),一個代理是一個接口的進程內實現。
服務端中,每一個特定的接口都建立了一份存根(stub)代碼(在服務器進程空間內)。存根是RPC傳輸的接收端,
它把代理傳過來的函數調用和參數傳遞映射為映射為服務器進程內的函數調用和參數傳遞。
客戶通過接口進行函數調用時,代理將參數打包為一個32位可移植的數據結構。打包完后,對服務器進程產生一個遠程過程調用。
服務器端,存根保存真正的接口指針、函數和相關的數據結構。存根將參數從包中解開以在本進程空間內調用。
一個函數調用返回時,存根將返回值和其他返回信息打包,發送回代理。代理解包,取出返回值供客戶使用。
類廠(class factory)
COM通過類廠來創建對象,進程空間內的每一個COM類都有一個類廠與之相對應。類廠產生的是對象,不是類。
類廠是一個特殊COM類。
一個重要的接口是 IClassFactory。它包含兩個函數:CreateInstance()和LockServer()。
客戶通過調用CreateInstance來創建OLE類的實例,LockServer來增加整個服務器的引用計數。
每一個COM類在源代碼一級都有自己的類廠。
進程內服務器通過一個預先定義的函數將類廠提供給客戶,
進程外服務器將類廠注冊到 Windows的注冊表中。
在進程內服務器提供類廠
通過導出函數(exported function)來提供類廠,函數名為:DLLGetClassObject()。
客戶可以通過 CoGetClassObject() 或 CoCreateInstance() 來創建一個 COM 對象。它們都調用了DLLGetClassObject來得到相應COM類的類廠接口指針。
然后可以調用類廠的CreateInstance() 來創建一個類的實例并且得到一個接口指針。
STDAPI DllGetClassObject ( REFCLSID rclsid, REFIID riid, LPVOID FAR* ppv)
要得到的類廠的COM類的GUID值,第三個參數 是一個指向接口指針的指針,用來存放請求的接口指針的地方。
在進程外服務器中提供類廠:
在運行時在注冊表里注冊類廠,CoRegisterClassObject():
HRESULT CoRegisterClassObject ( REFCLSID clsid, // 要注冊的類的GUID
IUnknown *pUnk, //對象的 IUnknown 指針
DWORD grfContext, //參數用來標識服務器的環境——可執行的服務器代碼是一個DLL、一個本地服務器還是一個遠程服務器
DWORD grfFlags, //用來表示客戶使用何種方式跟服務器建立連接
LPDWORD pdwRegister ); //指向DWORD的指針,所指向的值在類廠注冊的時候由COM來填寫。該值標識了類廠的注冊信息,可以通過
它然后調用 CoRevokeClassObject() 來取消類廠的注冊。
卸載進程內服務器
調用COM中的 CoFreeUnusedLibraries() 來實現這個功能,該函數通過調用DLL的DLLCanUnloadNow()來詢問是否能被卸載。
卸載進程外服務器
進程外服務器是自己卸載自己,
· 當客戶減少服務器的引用計數(通過調用IClassFactory::LockServer ( FALSE ),而且外部沒有對象了。
· 當客戶釋放最后一個對象的最后一個接口指針,并且服務器的鎖計數是0.
posted on 2010-03-15 23:28
Euan 閱讀(1023)
評論(0) 編輯 收藏 引用 所屬分類:
windows