_《COM_原理與應用》考點原理.doc_第1頁
_《COM_原理與應用》考點原理.doc_第2頁
_《COM_原理與應用》考點原理.doc_第3頁
_《COM_原理與應用》考點原理.doc_第4頁
_《COM_原理與應用》考點原理.doc_第5頁
已閱讀5頁,還剩8頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

COM 原理與應用學習筆記 - 第一部分 COM原理 COM 是什么-COM 是由 Microsoft 提出的組件標準,它不僅定義了組件程序之間進行交互的標準,并且也提供了組件程序運行所需的環(huán)境。在 COM 標準中,一個組件程序也被稱為一個模塊,它可以是一個動態(tài)鏈接庫,被稱為進程內組件(in-process component);也可以是一個可執(zhí)行程序(即 EXE 程序),被稱作進程外組件(out-of-process component)。一個組件程序可以包含一個或多個組件對象,因為 COM 是以對象為基本單元的模型,所以在程序與程序之間進行通信時,通信的雙方應該是組件對象,也叫做 COM 對象,而組件程序(或稱作 COM 程序)是提供 COM 對象的代碼載體。COM 對象不同于一般面向對象語言(如 C+ 語言)中的對象概念,COM 對象是建立在二進制可執(zhí)行代碼級的基礎上,而 C+ 等語言中的對象是建立在源代碼級基礎上的,因此 COM 對象是語言無關的。這一特性使用不同編程語言開發(fā)的組件對象進行交互成為可能。- COM 對象與接口-類似于 C+ 中對象的概念,對象是某個類(class)的一個實例;而類則是一組相關的數(shù)據(jù)和功能組合在一起的一個定義。使用對象的應用(或另一個對象)稱為客戶,有時也稱為對象的用戶。接口是一組邏輯上相關的函數(shù)集合,其函數(shù)也被稱為接口成員函數(shù)。按照習慣,接口名常是以“I”為前綴。對象通過接口成員函數(shù)為客戶提供各種形式的服務。在 COM 模型中,對象本身對于客戶來說是不可見的,客戶請求服務時,只能通過接口進行。每一個接口都由一個 128 位的全局唯一標識符(GUID,Global Unique Identifier)來標識。客戶通過 GUID 來獲得接口的指針,再通過接口指針,客戶就可以調用其相應的成員函數(shù)。與接口類似,每個組件也用一個 128 位 GUID 來標識,稱為 CLSID(class identifer,類標識符或類 ID),用 CLSID 標識對象可以保證(概率意義上)在全球范圍內的唯一性。實際上,客戶成功地創(chuàng)建對象后,它得到的是一個指向對象某個接口的指針,因為 COM 對象至少實現(xiàn)一個接口(沒有接口的 COM 對象是沒有意義的),所以客戶就可以調用該接口提供的所有服務。根據(jù) COM 規(guī)范,一個 COM 對象如果實現(xiàn)了多個接口,則可以從某個接口得到該對象的任意其他接口。從這個過程我們也可以看出,客戶與 COM 對象只通過接口打交道,對象對于客戶來說只是一組接口。- COM 進程模型-COM 所提供的服務組件對象在實現(xiàn)時有兩種進程模型:進程內對象和進程外對象。如果是進程內對象,則它在客戶進程空間中運行;如果是進程外對象,則它運行在同機器上的另一個進程空間或者在遠程機器的空間。進程內服務程序:服務程序被加載到客戶的進程空間,在 Windows 環(huán)境下,通常服務程序的代碼以動態(tài)連接庫(DLL)的形式實現(xiàn)。本地服務程序:服務程序與客戶程序運行在同一臺機器上,服務程序是一個獨立的應用程序,通常它是一個 EXE 文件。遠程服務程序:服務程序運行在與客戶不同的機器上,它既可以是一個 DLL 模塊,也可以是一個 EXE 文件。如果遠程服務程序是以 DLL 形式實現(xiàn)的話,則遠程機器會創(chuàng)建一個代理進程。雖然 COM 對象有不同的進程模型,但這種區(qū)別對于客戶程序來說是透明的,因此客戶程序在使用組件對象時可以不管這種區(qū)別的存在,只要遵照 COM 規(guī)范即可。然而,在實現(xiàn) COM 對象時,還是應該慎重選擇進程模型。進程內模型的優(yōu)點是效率高,但組件不穩(wěn)定會引起客戶進程崩潰,因此組件可能會危及客戶;(savetime 注:這里有點問題,如果組件不穩(wěn)定,進程外模型也同樣會出問題,可能是因為進程內組件和客戶同處一個地址空間,出現(xiàn)沖突的可能性比較大?)進程外模型的優(yōu)點是穩(wěn)定性好,組件進程不會危及客戶程序,一個組件進程可以為多個客戶進程提供服務,但進程外組件開銷大,而且調用效率相對低一點。- COM 可重用性-由于 COM 標準是建立在二進制代碼級的,因此 COM 對象的可重用性與一般的面向對象語言如 C+ 中對象的重用過程不同。對于 COM 對象的客戶程序來說,它只是通過接口使用對象提供的服務,它并不知道對象內部的實現(xiàn)過程,因此,組件對象的重用性可建立在組件對象的行為方式上,而不是具體實現(xiàn)上,這是建立重用的關鍵。COM 用兩種機制實現(xiàn)對象的重用。我們假定有兩個 COM 對象,對象1 希望能重用對象2 的功能,我們把對象1 稱為外部對象,對象2 稱為內部對象。(1)包容方式。對象1 包含了對象2,當對象1 需要用到對象2 的功能時,它可以簡單地把實現(xiàn)交給對象2 來完成,雖然對象1 和對象2 支持同樣的接口,但對象1 在實現(xiàn)接口時實際上調用了對象2 的實現(xiàn)。(2)聚合方式。對象1 只需簡單地把對象2 的接口遞交給客戶即可,對象1 并沒有實現(xiàn)對象2 的接口,但它把對象2 的接口也暴露給客戶程序,而客戶程序并不知道內部對象2 的存在。= 第二章 COM 對象模型= 全局唯一標識符 GUID-COM 規(guī)范采用了 128 位全局唯一標識符 GUID 來標識對象和接口,這是一個隨機數(shù),并不需要專門機構進行分配和管理。因為 GUID 是個隨機數(shù),所以并不絕對保證唯一性,但發(fā)生標識符相重的可能性非常小。從理論上講,如果一臺機器每秒產(chǎn)生 10000000 個 GUID,則可以保證(概率意義上)的 3240 年不重復)。GUID 在 C/C+ 中可以用這樣的結構來描述: typedef struct _GUID DWORD Data1; WORD Data2; WORD Data3; BYTE Data48; GUID;例:64BF4372-1007-B0AA-444553540000 可以如下定義一個 GUID: extern C const GUID CLSID_MYSPELLCHECKER = 0x54BF0093, 0x1048, 0x399D, 0xB0, 0xA3, 0x45, 0x33, 0x43, 0x90, 0x47, 0x47 ;Visual C+ 提供了兩個程序生成 GUID: UUIDGen.exe(命令行) 和 GUIDGen.exe(對話框)。COM 庫提供了以下 API 函數(shù)可以產(chǎn)生 GUID: HRESULT CoCreateGuid(GUID *pguid);如果創(chuàng)建 GUID 成功,則函數(shù)返回 S_OK,并且 pguid 將指向所得的 GUID 值。- COM 對象-在 COM 規(guī)范中,并沒有對 COM 對象進行嚴格的定義,但 COM 提供的是面向對象的組件模型,COM 組件提供給客戶的是以對象形式封裝起來的實體??蛻舫绦蚺c COM 程序進行交互的實體是 COM 對象,它并不關心組件模型的名稱和位置(即位置透明性),但它必須知道自己在與哪個 COM 對象進行交互。- COM 接口-從技術上講,接口是包含了一組函數(shù)的數(shù)據(jù)結構,通過這組數(shù)據(jù)結構,客戶代碼可以調用組件對象的功能。接口定義了一組成員函數(shù),這組成員函數(shù)是組件對象暴露出來的所有信息,客戶程序利用這些函數(shù)獲得組件對象的服務。通常我們把接口函數(shù)表稱為虛函數(shù)表(vtable),指向 vtable 的指針為 pVtable。對于一個接口來說,它的虛函數(shù)表是確定的,因此接口的成員函數(shù)個數(shù)是不變的,而且成員函數(shù)的先后先后順序也是不變的;對于每個成員函數(shù)來說,其參數(shù)和返回值也是確定的。在一個接口的定義中,所有這些信息都必須在二進制一級確定,不管什么語言,只要能支持這樣的內存結構描述,就可以使用接口。 接口指針 - pVtable - 指針函數(shù)1 - |-| m_Data1 指針函數(shù)2 - | 對象實現(xiàn) | m_Data2 指針函數(shù)3 - |-|每一個接口成員函數(shù)的第一個參數(shù)為指向對象實例的指針(=this),這是因為接口本身并不獨立使用,它必須存在于某個 COM 對象上,因此該指針可以提供對象實例的屬性信息,在被調用時,接口可以知道是對哪個 COM 對象在進行操作。在接口成員函數(shù)中,字符串變量必須用 Unicode 字符指針,COM 規(guī)范要求使用 Unicode 字符,而且 COM 庫中提供的 COM API 函數(shù)也使用 Unicode 字符。所以如果在組件程序內部使用到了 ANSI 字符的話,則應該進行兩種字符表達的轉換。當然,在即建立組件程序又建立客戶程序的情況下,可以使用自己定義的參數(shù)類型,只要它們與 COM 所能識別的參數(shù)類型兼容。Visual C+ 提供兩種字符串的轉換: namespace _com_util BSTR ConvertStringToBSTR(const char *pSrc) throw(_com_error); BSTR ConvertBSTRToString(BSTR pSrc) throw(_com_error); BSTR 是雙字節(jié)寬度字符串,它是最常用的自動化數(shù)據(jù)類型。- 接口描述語言 IDL-COM 規(guī)范在采用 OSF 的 DCE 規(guī)范描述遠程調用接口 IDL (interface description language,接口描述語言)的基礎上,進行擴展形成了 COM 接口的描述語言。接口描述語言提供了一種不依賴于任何語言的接口的描述方法,因此,它可以成為組件程序和客戶程序之間的共同語言。COM 規(guī)范使用的 IDL 接口描述語言不僅可用于定義 COM 接口,同時還定義了一些常用的數(shù)據(jù)類型,也可以描述自定義的數(shù)據(jù)結構,對于接口成員函數(shù),我們可以定義每個參數(shù)的類型、輸入輸出特性,甚至支持可變長度的數(shù)組的描述。IDL 支持指針類型,與 C/C+ 很類似。例如: interface IDictionary HRESULT Initialize() HRESULT LoadLibrary(in string); HRESULT InsertWord(in string, in string); HRESULT DeleteWord(in string); HRESULT LookupWord(in string, out string *); HRESULT RestoreLibrary(in string); HRESULT FreeLibrary(); Microsoft Visual C+ 提供了 MIDL 工具,可以把 IDL 接口描述文件編譯成 C/C+ 兼容的接口描述頭文件(.h)。- IUnknown 接口-IUnknown 的 IDL 定義: interface IUnknown HRESULT QueryInterface(in REFIID iid, out void *ppv); ULONG AddRef(void); ULONG Release(void); IUnkown 的 C+ 定義: class IUnknown virutal HRESULT _stdcall QueryInterface(const IID& iid, void *ppv) = 0; virtual ULONG _stdcall AddRef() = 0; virutal ULONG _stdcall Release() = 0; - COM 對象的接口原則-COM 規(guī)范對 QueryInterface 函數(shù)設置了以下規(guī)則:1. 對于同一個對象的不同接口指針,查詢得到的 IUnknown 接口必須完全相同。也就是說,每個對象的 IUnknown 接口指針是唯一的。因此,對兩個接口指針,我們可以通過判斷其查詢到的 IUnknown 接口是否相等來判斷它們是否指向同一個對象。2. 接口自反性。對一個接口查詢其自身總應該成功,比如: pIDictionary-QueryInterface(IID_Dictionary, .) 應該返回 S_OK。3. 接口對稱性。如果從一個接口指針查詢到另一個接口指針,則從第二個接口指針再回到第一個接口指針必定成功,比如: pIDictionary-QueryInterface(IID_SpellCheck, (void *)&pISpellCheck); 如果查找成功的話,則再從 pISpellCheck 查回 IID_Dictionary 接口肯定成功。4. 接口傳遞性。如果從第一個接口指針查詢到第二個接口指針,從第二個接口指針可以查詢到第三個接口指針,則從第三個接口指針一定可以查詢到第一個接口指針。5. 接口查詢時間無關性。如果在某一個時刻可以查詢到某一個接口指針,則以后任何時間再查詢同樣的接口指針,一定可以查詢成功??傊?,不管我們從哪個接口出發(fā),我們總可以到達任何一個接口,而且我們也總可以回到最初的那個接口。= 第三章 COM 的實現(xiàn)= COM 組件注冊信息- 當前機器上所有組件的信息 HKEY_CLASS_ROOT/CLSID 進程內組件 HKEY_CLASS_ROOT/CLSID/guid/InprocServer32 進程外組件 HKEY_CLASS_ROOT/CLSID/guid/LocalServer32 組件所屬類別(CATID) HKEY_CLASS_ROOT/CLSID/guid/Implemented Categories COM 接口的配置信息 HKEY_CLASS_ROOT/Interface 代理 DLL/存根 DLL HKEY_CLASS_ROOT/CLSID/guid/ProxyStubClsid HKEY_CLASS_ROOT/CLSID/guid/ProxyStubClsid32 類型庫的信息 HKEY_CLASS_ROOT/TypeLib 字符串命名 ProgID HKEY_CLASS_ROOT/ (例如 COMCTL.TreeCtrl) 組件 GUID HKEY_CLASS_ROOT/COMTRL.TreeControl/CLSID 缺省版本號 HKEY_CLASS_ROOT/COMTRL.TreeControl/CurVer (例如 CurVer = COMTRL.TreeCtrl.1, 那么 HKEY_CLASS_ROOT/COMTRL.TreeControl.1 也存在) 當前機器所有組件類別 HKEY_CLASS_ROOT/Component CategoriesCOM 提供兩個 API 函數(shù) CLSIDFromProgID 和 ProgIDFromCLSID 轉換 ProgID 和 CLSID。如果 COM 組件支持同樣一組接口,則可以把它們分到同一類中,一個組件可以被分到多個類中。比如所有的自動化對象都支持 IDispatch 接口,則可以把它們歸成一類“Automation Objects”。類別信息也用一個 GUID 來描述,稱為 CATID。組件類別最主要的用處在于客戶可以快速發(fā)現(xiàn)機器上的特定類型的組件對象,否則的話,就必須檢查所有的組件對象,并把組件對象裝入到內存中實例化,然后依次詢問是否實現(xiàn)了必要的接口,現(xiàn)在使用了組件類別,就可以節(jié)省查詢過程。- 注冊 COM 組件-RegSrv32.exe 用于注冊一個進程內組件,它調用 DLL 的 DllRegisterServer 和 DllUnregisterServer 函數(shù)完成組件程序的注冊和注銷操作。如果操作成功返回 TRUE,否則返回 FALSE。對于進程外組件程序,情形稍有不同,因為它自身是個可執(zhí)行程序,而且它也不能提供入口函數(shù)供其他程序使用。因此,COM 規(guī)范中規(guī)定,支持自注冊的進程外組件必須支持兩個命令行參數(shù) /RegServer 和 /UnregServer,以便完成注冊和注銷操作。命令行參數(shù)大小寫無關,而且 “/” 可以用 “-” 替代。如果操作成功,程序返回 0,否則,返回非 0 表示失敗。- 類廠和 DllGetObjectClass 函數(shù)-類廠(class factory)是 COM 對象的生產(chǎn)基地,COM 庫通過類廠創(chuàng)建 COM 對象;對應每一個 COM 類,有一個類廠專門用于該 COM 類的對象創(chuàng)建操作。類廠本身也是一個 COM 對象,它支持一個特殊的接口 IClassFactory:class IClassFactory : public IUnknown virtual HRESULT _stdcall CreateInstance(IUnknown *pUnknownOuter, const IID& iid, void *ppv) = 0; virtual HRESULT _stdcall LockServer(BOOL bLock) = 0;CreateInstance 成員函數(shù)用于創(chuàng)建對應的 COM 對象。第一個參數(shù) pUnknownOuter 用于對象類被聚合的情形,一般設置為 NULL;第二個參數(shù) iid 是對象創(chuàng)建完成后客戶應該得到的初始接口 IID;第三個參數(shù) ppv 存放返回的接口指針。LockServer 成員函數(shù)用于控制組件的生存周期。類廠對象是由 DLL 引出函數(shù) DllGetClassObject 創(chuàng)建的: HRESULT DllGetClassObject(const CLSID& clsid, const IID& iid, (void *)ppv);DllGetClassObject 函數(shù)的第一個參數(shù)為待創(chuàng)建對象的 CLSID。因為一個組件可能實現(xiàn)了多個 COM 對象類,所以在 DllGetClassObject 函數(shù)的參數(shù)中有必要指定 CLSID,以便創(chuàng)建正確的 class factory。另兩個參數(shù) iid 和 ppv 分別指于指定接口 IID 和存放類廠接口指針。COM 庫在接到對象創(chuàng)建的指令后,它要調用進程內組件的 DllGetClassObject 函數(shù),由該函數(shù)創(chuàng)建類廠對象,并返回類廠對象的接口指針。COM 庫或客戶一旦擁有類廠的接口指針,它們就可以通過 IClassFactory 的成員函數(shù) CreateInstance 創(chuàng)建相應的 COM 對象。- CoGetClassObject 函數(shù)-在 COM 庫中,有三個 API 可用于對象的創(chuàng)建,它們分別是 CoGetClassObject、CoCreateInstnace 和 CoCreateInstanceEx。通常情況下,客戶程序調用其中之一完成對象的創(chuàng)建,并返回對象的初始接口指針。COM 庫與類廠也通過這三個函數(shù)進行交互。 HRESULT CoGetClassObject(const CLSID& clsid, DWORD dwClsContext, COSERVERINFO *pServerInfo, const IID& iid, (void *)ppv);CoGetClassObject 函數(shù)先找到由 clsid 指定的 COM 類的類廠,然后連接到類廠對象,如果需要的話,CoGetClassObject 函數(shù)裝入組件代碼。如果是進程內組件對象,則 CoGetClassObject 調用 DLL 模塊的 DllGetClassObject 引出函數(shù),把參數(shù) clsid、iid 和 ppv 傳給 DllGetClassObject 函數(shù),并返回類廠對象的接口指針。通常情況下 iid 為 IClassFactory 的標識符 IID_IClassFactory。如果類廠對象還支持其它可用于創(chuàng)建操作的接口,也可以使用其它的接口標識符。例如,可請求 IClassFactory2 接口,以便在創(chuàng)建時,驗證用戶的許可證情況。IClassFactory2 接口是對 IClassFactory 的擴展,它加強了組件創(chuàng)建的安全性。參數(shù) dwClsContext 指定組件類別,可以指定為進程內組件、進程外組件或者進程內控制對象(類似于進程外組件的代理對象,主要用于 OLE 技術)。參數(shù) iid 和 ppv 分別對應于 DllGetClassObject 的參數(shù),用于指定接口 IID 和存放類對象的接口指針。參數(shù) pServerInfo 用于創(chuàng)建遠程對象時指定服務器信息,在創(chuàng)建進程內組件對象或者本地進程外組件時,設置 NULL。如果 CoGetClassObject 函數(shù)創(chuàng)建的類廠對象位于進程外組件,則情形要復雜得多。首先 CoGetClassObject 函數(shù)啟動組件進程,然后一直等待,直到組件進程把它支持的 COM 類對象的類廠注冊到 COM 中。于是 CoGetClassObject 函數(shù)把 COM 中相應的類廠信息返回。因此,組件外進程被 COM 庫啟動時(帶命令行參數(shù)“/Embedding”),它必須把所支持的 COM 類的類廠對象通過 CoRegisterClassObject 函數(shù)注冊到 COM 中,以便 COM 庫創(chuàng)建 COM 對象使用。當進程退出時,必須調用 CoRevokeClassObject 函數(shù)以便通知 COM 它所注冊的類廠對象不再有效。組件程序調用 CoRegisterClassObject 函數(shù)和 CoRevokeClassObject 函數(shù)必須配對,以保證 COM 信息的一致性。- CoCreateInstance / CoCreateInstanceEx 函數(shù)- HRESULT CoCreateInstance(const CLSID& clsid, IUnknown *pUnknownOuter, DWORD dwClsContext, const IID& iid, (void *)ppv);CoCreateInstance 是一個被包裝過的輔助函數(shù),在它的內部實際上也調用了 CoGetClassObject 函數(shù)。CoCreateInstance 的參數(shù) clsid 和 dwClsContext 的含義與 CoGetClassObject 相應的參數(shù)一致,(CoCreateInstance 的 iid 和 ppv 參數(shù)與 CoGetClassObject 不同,一個是表示對象的接口信息,一個是表示類廠的接口信息)。參數(shù) pUnknownOuter 與類廠接口的 CreateInstance 中對應的參數(shù)一致,主要用于對象被聚合的情況。CoCreateInstance 函數(shù)把通過類廠創(chuàng)建對象的過程封裝起來,客戶程序只要指定對象類的 CLSID 和待輸出的接口指針及接口 ID,客戶程序可以不與類廠打交道。CoCreateInstance 可以用下面的代碼實現(xiàn):(savetime 注:下面代碼中 ppv 指針的應用,好像應該是 void *) HRESULT CoCreateInstance(const CLSID& clsid, IUnknown *pUnknownOuter, DWORD dwClsContext, const IID& iid, void *ppv) IClassFactory *pCF; HRESULT hr; hr = CoGetClassObject(clsid, dwClsContext, NULL, IID_IClassFactory, (void *) pCF); if (FAILED(hr) return hr; hr = pCF-CreateInstance(pUnknownOuter, iid, (void *)ppv); pFC-Release(); return hr; 從這段代碼我們可以看出,CoCreateInstance 函數(shù)首先利用 CoGetClassObject 函數(shù)創(chuàng)建類廠對象,然后用得到的類廠對象的接口指針創(chuàng)建真正的 COM 對象,最后把類廠對象釋放掉并返回,這樣就把類廠屏蔽起來。但是,用 CoCreateInstance 并不能創(chuàng)建遠程機器上的對象,因為在調用 CoGetClassObject 時,把第三個用于指定服務器信息的參數(shù)設置為 NULL。如果要創(chuàng)建遠程對象,可以使用 CoCreateInstance 的擴展函數(shù) CoCreateInstanceEx: HRESULT CoCreateInstanceEx(const CLSID& clsid, IUnknown *pUnknownOuter, DWORD dwClsContext, COSERVERINFO *pServerInfo, DWORD dwCount, MULTI_QI *rgMultiQI);前三個參數(shù)與 CoCreateInstance 一樣,pServerInfo 與 CoGetClassOjbect 的參數(shù)一樣,用于指定服務器信息,最后兩個參數(shù) dwCount 和 rgMultiQI 指定了一個結構數(shù)組,可以用于保存多個對象接口指針,其目的在于一次獲得多個接口指針,以便減少客戶程序與組件程序之間的頻繁交互,這對于網(wǎng)絡環(huán)境下的遠程對象是很有意義的。- COM 庫的初始化-調用 COM 庫的函數(shù)之前,為了使函數(shù)有效,必須調用 COM 庫的初始化函數(shù): HRESULT CoInitialize(IMalloc *pMalloc);pMalloc 用于指定一個內存分配器,可由應用程序指定內存分配原則。一般情況下,我們直接把參數(shù)設為 NULL,則 COM 庫將使用缺省提供的內存分配器。返回值:S_OK 表示初始化成功 S_FALSE 表示初始化成功,但這次調用不是本進程中首次調用初始化函數(shù) S_UNEXPECTED 表示初始化過程中發(fā)生了錯誤,應用程序不能使用 COM 庫通常,一個進程對 COM 庫只進行一次初始化,而且,在同一個模塊單元中對 COM 庫進行多次初始化并沒有意義。唯一不需要初始化 COM 庫的函數(shù)是獲取 COM 庫版本的函數(shù): DWORD CoBuildVersion();返回值:高 16 位 主版本號 低 16 位 次版本號COM 程序在用完 COM 庫服務之后,通常是在程序退出之前,一定要調用終止 COM 庫服務函數(shù),以便釋放 COM 庫所維護的資源: void CoUninitialize(void);注意:凡是調用 CoInitialize 函數(shù)返回 S_OK 的進程或程序模塊一定要有對應的 CoUninitialize 函數(shù)調用,以保證 COM 庫有效地利用資源。(? 如果在一個模塊中調用 CoInitialize 返回 S_OK,那么它調用 CoUnitialize 函數(shù)后,其它也在使用 COM 庫的模塊是否會出錯誤?還是 COM 庫會自動檢查有哪些模塊在使用?)- COM 庫的內存管理-由于 COM 組件程序和客戶程序是通過二進制級標準建立連接的,所以在 COM 應用程序中凡是涉及客戶、COM 庫和組件三者之間內存交互(分配和釋放不在同一個模塊中)的操作必須使用一致的內存管理器。COM 提供的內存管理標準,實際上是一個 IMalloc 接口: / IID_IMalloc: 00000002-0000-0000-C000-000000000046 class IMalloc: public IUnknown void * Alloc(ULONG cb) = 0; void * Realloc(void *pv, ULONG cb) = 0; void Free(void *pv) = 0; ULONG GetSize(void *pv) = 0; / 返回分配的內存大小 int DidAlloc(void *pv) = 0; / 確定內存指針是否由該內存管理器分配 void HeapMinimize() = 0; / 使堆內存盡可能減少,把沒用到的內存還給 / 操作系統(tǒng),用于性能優(yōu)化 獲得 IMalloc 接口指針: HRESULT CoGetMalloc(DWORD dwMemContext, IMalloc *ppMalloc);CoGetMalloc 函數(shù)的第一個參數(shù) dwMemContext 用于指定內存管理器的類型。COM 庫中包含兩種內存管理器,一種就是在初始化時指定的內存管理器或者其內部缺省的管理器,也稱為作業(yè)管理器(task allocator),這種管理器在本進程內有效,要獲取該管理器,在 dwMemContext 參數(shù)中指定為 MEMCTX_TASK;另一種是跨進程的共享分配器,由 OLE 系統(tǒng)提供,要獲取這種管理器,dwMemContext 參數(shù)中指定為 MEMCTX_SHARED,使用共享管理器的便利是,可以在一個進程內分配內存并傳給第二個進程,在第二個進程內使用此內存甚至釋放掉此內存。只要函數(shù)的返回值為 S_OK,則 ppMalloc 就指向了 COM 庫的內存管理器接口指針,可以使用它進行內存操作,使用完畢后,應該調用 Release 成員函數(shù)釋放控制權。COM 庫封裝了三個 API 函數(shù),可用于內存分配和釋放: void * CoTaskMemAlloc(ULONG cb); void CoTaskFree(void *pv); void CoTaskMemRealloc(void *pv, ULONG cb);這三個函數(shù)分配對應于 IMalloc 的三個成員函數(shù):Alloc、Realloc 和 Free。例:COM 程序如何從 CLSID 值找到相應的 ProgID 值: WCHAR *pwProgID; char pszProgID128; hResult = :ProgIDFromCLSID(CLSID_Dictionary, &pwProgID); if (hResult != S_OK) . wcstombs(pszProgID, pwProgID, 128); CoTaskMemFree(pwProgID); / 注意:必須釋放內存在調用 COM 函數(shù) ProgIDFromCLSID 返回之后,因為 COM 庫為輸出變量 pwProgID 分配了內存空間,所以應用程序在用完 pwProgID 變量之后,一定要調用 CoTaskMemFree 函數(shù)釋放內存。該例子說明了在 COM 庫中分配內存,而在調用程序中釋放內存的一種情況。COM 庫中其他一些函數(shù)也有類似的特性,尤其是一些包含不定長度輸出參數(shù)的函數(shù)。- 組件程序的裝載和卸載-進程內組件的裝載:客戶程序調用COM 庫的 CoCreateInstance 或 CoGetClassObject 函數(shù)創(chuàng)建 COM 對象,在 CoGetClassObject 函數(shù)中,COM 庫根據(jù)系統(tǒng)注冊表中的信息,找到類標識符 CLSID 對應的組件程序(DLL 文件)的全路徑,然后調用 LoadLibrary(實際上是 CoLoadLibrary)函數(shù),并調用組件程序的 DllGetClassObject 引出函數(shù)。DllGetClassObject 函數(shù)創(chuàng)建相應的類廠對象,并返回類廠對象的 IClassFactory 接口。至此 CoGetClassObject 函數(shù)的任務完成,然后客戶程序或者 CoCreateInstance 函數(shù)繼續(xù)調用類廠對象的 CreateInstance 成員函數(shù),由它負責 COM 對象的創(chuàng)建工作。 CoCreateInstance |-CoGetClassObject |-Get CLSID - DLLfile path |-CoLoadLibrary |-DLLfile.DllGetClassObject |-return IClassFactory |-IClassFactory.CreateInstnace進程外組件的裝載:在 COM 庫的 CoGetClassObject 函數(shù)中,當它發(fā)現(xiàn)組件程序是 EXE 文件(由注冊表組件對象信息中的 LocalServer 或 LocalServer32 值指定)時,COM 庫創(chuàng)建一個進程啟動組件程序,并帶上“/Embedding”命令行參數(shù),然后等待組件程序;而組件程序在啟動后,當它檢查到“/Embedding”命令行參數(shù)后,就會創(chuàng)建類廠對象,然后調用 CoRegisterClassObject 函數(shù)把類廠對象注冊到 COM 中。當 COM 庫檢查到組件對象的類廠之后,CoGetClassObject 函數(shù)就把類廠對象返回。由于類廠與客戶程序運行在不同的進程中,所以客戶程序得到的是類廠的代理對象。一旦客戶程序或 COM 庫得到了類廠對象,它就可以完成組件對象的創(chuàng)建工作。進程內對象和進程外對象的不同創(chuàng)建過程僅僅影響了 CoGetClassObject 函數(shù)的實現(xiàn)過程,對于客戶程序來說是完全透明的。 CoGetClassObject |-LocalServer/LocalServer32 |-Execute EXE /Embedding |-Create class factory |-CoRegisterClassObject ( class factory ) |-return class factory (proxy)進程內組件的卸載:只有當組件程序滿足了兩個條件時,它才能被卸載,這兩個條件是:組件中對象數(shù)為 0,類廠的鎖計數(shù)為 0。滿足這兩個條件時,DllCanUnl

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論