版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、第1章 COM 基本概念 1.1 什么是 COM所謂 COM( Componet Object Model ,組件對(duì)象模型) ,是一種說明如何建立可動(dòng)態(tài)互變 組件的規(guī)范, 此規(guī)范提供了為保證能夠互操作, 客戶和組件應(yīng)遵循的一些二進(jìn)制和網(wǎng)絡(luò)標(biāo)準(zhǔn)。 通過這種標(biāo)準(zhǔn)將可以在任意兩個(gè)組件之間進(jìn)行通信而不用考慮其所處的操作環(huán)境是否相同、 使用的開發(fā)者語言是否一致以及是否運(yùn)行于同一臺(tái)計(jì)算機(jī)。COM規(guī)范所描述的即是如何編寫組件,遵循COM標(biāo)準(zhǔn)的任何一個(gè)組件都是可以被用來組合成應(yīng)用程序的。 至于對(duì)組件采取的是何種編程語言則是無關(guān)緊要的, 可以自由選取。 作 為一個(gè)真正意義上的組件,應(yīng)具備如下特征:1)實(shí)現(xiàn)對(duì)開發(fā)
2、語言的封裝。2)以二進(jìn)制形式發(fā)布。3)能夠在不妨礙已有用戶的情況下被升級(jí)。4)在網(wǎng)絡(luò)上的位置必須能夠被透明的重新分配。在 Windows 操作系統(tǒng)平臺(tái)上,有一些用 COM 形式提供的組件模塊極大地豐富了 Windows 的功能,而且也使 Windows 功能擴(kuò)展更加靈活,例如:1)DiretX 多媒體軟件包。 它以 COM 接口的形式為 Windows 平臺(tái)提供了強(qiáng)大的多媒體 功能,現(xiàn)廣泛用于游戲娛樂軟件及其他多媒體軟件的開發(fā)。2)RDO( remote data objet,遠(yuǎn)程數(shù)據(jù)對(duì)象)和 DAO( data access object,數(shù)據(jù)訪問對(duì)象)數(shù)據(jù)庫訪問對(duì)象庫。 它以 COM 自動(dòng)
3、化對(duì)象的形式為數(shù)據(jù)庫應(yīng)用提供了便捷的操作 方法,特別適合于在BASIC語言或其他一些高級(jí)語言中使用。而數(shù)據(jù)訪問一致接口 OLE DB/ADO( active data object ,活動(dòng)數(shù)據(jù)對(duì)象)更淋漓盡致地發(fā)揮了 COM 接 口的作用。3)In ternet Clie nt SDK。它提供了一組 COM庫,為應(yīng)用系統(tǒng)增加In ternet特性提供了底 層透明的一致操作。其它還有一些組件如 MAPI( messaging API,消息應(yīng)用編程接口) 、ADSI( active directory service interface ,活動(dòng)目錄服務(wù)接口) 等,它們都提供了一致、 高效的服務(wù)。
4、從整個(gè) Windows 操作系統(tǒng)來看, COM 成了系統(tǒng)的基本軟件模型,它帶來的是靈活性和高效率,以及應(yīng)用開 發(fā)的一致性。 1.2 COM對(duì)象與C+對(duì)象比較COM對(duì)象建立在二進(jìn)制一級(jí)的基礎(chǔ)上,而C+對(duì)象建立在源代碼一級(jí)的基礎(chǔ)上,但從特性上,可以作一比較。1)封裝性:COM 對(duì)象的數(shù)據(jù)成員的封裝以組件模型為最終邊界,對(duì)于用戶是完全透明的、不可見 的;而C+對(duì)象的封裝特性只是語義上的封裝,對(duì)于對(duì)象用戶是可見的。2)可重用性COM 對(duì)象的可重用性表現(xiàn)在 COM 對(duì)象的包容和聚合, 一個(gè)對(duì)象可以完全使用另一個(gè)另 一個(gè)對(duì)象的所有功能;而 C+對(duì)象的可重用表現(xiàn)在 C+類的繼承性,派生類可以調(diào)用其父類 的非
5、私有成員函數(shù)。3)多態(tài)性C+對(duì)象的多態(tài)性體現(xiàn)了 C+語言用類描述事物的高度抽象的特征;COM對(duì)象也具有多態(tài)性,但這種多態(tài)性需要通過COM對(duì)象所具有的接口才能體現(xiàn)出來,就像C+對(duì)象的多態(tài)性需要通過其虛函數(shù)才能體現(xiàn)一樣。 1.3 COM對(duì)象和接口COM提供的是面向?qū)ο蟮慕M件模型,COM組件提供給客戶的是以對(duì)象形式封裝起來的實(shí)體??蛻舫绦蚝?COM組件程序進(jìn)行交互的實(shí)體是 COM對(duì)象。COM對(duì)象包括屬性(也稱為狀態(tài))和方法(也稱為操作),對(duì)象的狀態(tài)反映了對(duì)象的存在,也是區(qū)別于其他對(duì)象的要素;而對(duì)象所提供的方法就是對(duì)象提供給外界的接口,客戶必須通過接口才能獲得對(duì)象的服務(wù)。對(duì)于COM對(duì)象來說,接口是它與
6、外界進(jìn)行交互的唯一途徑,因此,封裝特性是 COM對(duì)象的基本特征。1.3.1 COM對(duì)象標(biāo)識(shí)CLSIDCOM組件的位置對(duì)于客戶來說是透明的,因?yàn)榭蛻舨⒉恢苯尤ピL問 COM組件,客戶程序通過一個(gè)全局標(biāo)識(shí)符進(jìn)行對(duì)象的創(chuàng)建和初始化工作,這個(gè)全局標(biāo)識(shí)符就是CLSID CLSID結(jié)構(gòu)定義上與 GUID 一致。COM規(guī)范采用了 128位全局唯一標(biāo)示符 GUID。這是一個(gè)隨機(jī)數(shù)。手工創(chuàng)建128位GUID 或者編寫程序來產(chǎn)生 GUID是件很麻煩的事。為此,Microsoft Visual C+提供了兩個(gè)工具實(shí)現(xiàn) 這樣的目的:UUIDGen.exe和GUIDGen.exe,前者是一個(gè)命令行程序,后者是一個(gè)基于對(duì)話
7、 框的應(yīng)用程序。另外,COM庫為我們提供了以下 API函數(shù)可以產(chǎn)生 GUID:HRESULT CoCreateGuid( GUID *pguid)下面為示例工程中.rgs文件中CLSID的定義:Math.Obj.1 = s MyMath ClassCLSID = s 3B28F0D6-D029-484B-80D7-A946EB20E9BD將示例工程的COM組件成功注冊(cè)后,我們可以根據(jù)組件的CLSID在系統(tǒng)的注冊(cè)表編輯器中找到組件的注冊(cè)信息,如圖1。F _| 3B20fQD6-DO2-484E-8OI)7-A946EE2OE9BD) I l InprocServ4r32- I FroglDLJ
8、TypeLib l 1 Vtrsi onI_| Ver s i ?nlhdep sndsntFr: gIL- - -圖1系統(tǒng)注冊(cè)表1.3.2 COM對(duì)象的數(shù)據(jù)類型HRESULT 個(gè)雙字節(jié)的值,其最高位(bit)如果是0表示成功,1表示錯(cuò)誤。HRESULT值含義S OK0x00000000成功S FALSE0x00000001函數(shù)成功執(zhí)行完成,但返回時(shí)出現(xiàn)錯(cuò)誤E INVALIDARG0x80070057參數(shù)有錯(cuò)誤E OUTOFMEMORY0x8007000E內(nèi)存申請(qǐng)錯(cuò)誤E UNEXPECTED0x8000FFFF未知的異常E NOTIMPL0x80004001未實(shí)現(xiàn)功能E FAIL0x80004
9、005沒有詳細(xì)說明的錯(cuò)誤E POINTER0x80004003無效的指針E HANDLE0x80070006無效的句柄E ABORT0x80004004終止操作E ACCESSDENIED0x80070005訪問被拒絕E NOINTERFACE0x80004002不支持接口UNICODEIDL字符串的標(biāo)準(zhǔn)形式,使用2個(gè)字節(jié)表示一個(gè)字符(unsigned short int、WCHAR _wchar_t、OLECHAR,不會(huì)出現(xiàn)亂碼, UNICODE的范圍是 0x0000-0xFFFF共 6 萬多個(gè)字符。BSTR 個(gè)OLECHAR類型的Unicode字符串。由于操作系統(tǒng)提供相應(yīng)的 API函數(shù)(如
10、 SysAllocString)來管理它以及一些默認(rèn)的調(diào)度代碼,因此BSTR實(shí)際上就是一個(gè)COM字符串,自帶字符串長(zhǎng)度信息。100000000065006C006C006F002C0D604F7D590000長(zhǎng)度北字節(jié)He11D你好-BSTRAPI函數(shù)函數(shù)作用SysAllocStri ng()申請(qǐng)一個(gè)BSTR指針,并初始化為一個(gè)字符串SysFreeStri ng()釋放BSTR內(nèi)存SysAllocStri ngLe n()申請(qǐng)一個(gè)指定字符長(zhǎng)度的BSTR指針,并初始化為一個(gè)字符串SysAllocStri ngByteLe n()申請(qǐng)一個(gè)指定字節(jié)長(zhǎng)度的BSTR指針,并初始化為一個(gè)字符串SysReA
11、llocStri ngLe n()重新申請(qǐng)BSTR指針CString 函數(shù)函數(shù)作用AllocSysStri ng()從 CString 得至U BSTRSetSysStri ng()重新申請(qǐng)BSTR指針,并復(fù)制到 CString中CComBSTR函數(shù)ATL的BSTR包裝類,在 atlbase.h中定義,具體查看 MSDN_bstr_tC+寸BSTR的封裝,它的構(gòu)造和析構(gòu)函數(shù)分別調(diào)用SysAllocString和SysFreeString函數(shù),其他操作是借用 BSTR AP函數(shù)。VARIANT具有跨語言的特性,它可以表示(存儲(chǔ))任意類型的數(shù)據(jù),既包括了數(shù) 據(jù)本身,也包含了數(shù)據(jù)的類型,定義在 oa
12、idl.h中。struct tagVARIANTVARTYPE vt;Un io nshortiVal;/VT_I2longlVal;/VT_I4floatfltVal;/VT_R4doubledblVal;/VT_R8DATEdate;/VT_DATEBSTRbstrVal;/VT_BSTRshort*piVal;/VT_BYREF|VT_I2lon g*plVal;/VT_BYREF|VT_I4float*pfltVal;/VT_BYREF|VT_R4double*pdbVal;/VT_BYREF|VT_R8DATE*pdate;/VT_BYREF|VT_DATEBSTR*pbstrVal
13、;/VT_BYREF|VT_BSTR;typedef tagVARIANT VARIANT;VARIANT使用示例:VARIANT va;:Varia ntIn it (&va);初始化Int a = 2012;Va.vt = VT_I4;指明Iong數(shù)據(jù)類型Va.IVaI = a;/賦值:Varia ntCIear();Windows定義的VARIANT相關(guān)函數(shù):Varia ntI nt將變量初始化為 VT_EMPTY;Varia ntCIear消除并初始化VARIANT;Varia ntCha ngeType改變VARIANT的類型;Varia ntCopy釋放與目標(biāo) VARIANT相連的
14、內(nèi)存并賦值源 VARIANTCOIeVariant:對(duì) VARIANT結(jié)構(gòu)的封裝COIeVaria ntCOIeVaria ntCOIeVaria ntv1( This is a test ); v2 = This is a test; v3(Io ng)2012);直接構(gòu)造/結(jié)果時(shí) VT_BSTR 類型,值為”This is a test”COIeVaria ntv4 = (Ion g)2012;結(jié)果時(shí)VT_I4類型,值為2012_variant_t:用于 COM 的 VARIANT類 1.4描述性語言IDL和MIDL編譯器COM規(guī)范在采用 OSF的DCE規(guī)范描述遠(yuǎn)程調(diào)用接口IDL( in
15、terface description Ian guage,接口描述語言)的基礎(chǔ)上,進(jìn)行擴(kuò)展形成了COM接口的描述語言。接口描述語言提供了一一種不依賴于任何語言的接口描述方法,因此,它可以成為組件程序和客戶端程序之間的共同語言。COM規(guī)范使用的IDL接口描述語言不僅可用于定義 COM接口,同時(shí)還定義了一些常用 的數(shù)據(jù)類型,也可以描述自定義的數(shù)據(jù)結(jié)構(gòu), 對(duì)于接口成員函數(shù), 我們可以指定每個(gè)參數(shù)的 類型、輸入輸出特性,甚至支持可變長(zhǎng)度的數(shù)組的描述。IDL中所有數(shù)據(jù)、方法、接口、類和庫的特性都由屬性信息來描述。屬性信息中由括號(hào) 括起來,作為它們描述的對(duì)象的前綴。1) in:輸入型參數(shù),從調(diào)用者傳遞到
16、被調(diào)用者,被調(diào)用者對(duì)輸入型參數(shù)的更改不傳回 調(diào)用者。2) out:輸出型參數(shù),從被調(diào)用者返回調(diào)用者,而被調(diào)用者不關(guān)心參數(shù)的初始值。3) In,out:輸入輸出型參數(shù)在調(diào)用的時(shí)候傳到被調(diào)用者,同時(shí),被調(diào)用者可以對(duì)參數(shù)進(jìn)行修改,這個(gè)修改在調(diào)用返回的時(shí)候會(huì)被復(fù)制回調(diào)用者。(PS:非指針類型一定是輸入型參數(shù)。輸出型參數(shù)和輸入輸出型參數(shù)一定是指針類型)4) retvaI:返回一個(gè)與方法的物理 HRESULT不相關(guān)的邏輯結(jié)果, 與out 起使用,且只 能有一個(gè),放在參數(shù)的最后。5) string:參數(shù)所指向的是一個(gè)字符串類型參數(shù),以Null終止。6) size_is:指針數(shù)組中元素個(gè)數(shù)由另一個(gè)參數(shù)說明。7
17、) length_is:用來設(shè)置在序列化時(shí)需要復(fù)制的元素?cái)?shù)量。下面IDL示例為工程 Math的IDL文件。IDL中接口定義示例:object,uuid (CED2CE33-5419-49E0-AE72-CB1E4D2B0C8F),oleautomati on,non exte nsible,poin ter_default(u ni que)in terface IMyMath : IU nknownhelpstring(方法 Add) HRESULT Add( in Element* pElement, out DWORD* pValue);helpstring(方法 Operate) HR
18、ESULT Operate( in Element* pElement, function fun, out DWORD* pValue);helpstring(方法 Sum) HRESULT Sum( in DWORD ColCount,in DWORD RowCou nt,in, size_is(ColCount*RowCount)DWORD* pNums,out, retval DWORD* pResult);IDL中enum定義示例:typedefuuid (9CE9F449-3894-44AD-9F15-1DE67E915329),versio n(1.0),helpstring(E
19、num of function)enum fun cti onfAdd = 0,fSub,fMulfun cti on;IDL中struct定義示例:typedefuuid (9CE9F449-3894-44AD-9F15-1DE67E915329),versio n(1.0),helpstring(Enum of function)enum fun cti onfAdd = 0,fSub,fMulfun cti on;IDL中union定義示例:typedefuuid(994A75FF-6FC8-4802-AA42-4E04776BD521),versio n(1.0),helpstring
20、( “ NUMBER)un io n NUMBER case(1) I ong i;case (2) float f;NUMBER;IDL類定義示例: uuid(12881436-9C8F-457E-851F-25CCD3F25D30), versio n(1.0),library MathLibimportlib (stdole2.tlb);uuid(3B28F0D6-D029-484B-80D7-A946EB20E9BD) coclassMyMathdefault in terface IMyMath;in terface IMyMath2;Microsoft Visual C+提供了 M
21、IDL工具,可以把IDL接口描述文件編譯成 C/C+兼容的接 口描述文件(.h)和C文件(.c),可以被組件程序和客戶程序所使用。XX_i.h: 個(gè)同C和C+兼容的,包含IDL中所描述的所有接口聲明的頭文件;XX_i.c: 個(gè)定義有IDL文件中所用的所有 GUID的C文件 1.5 IUnknown 接口COM定義的每一個(gè)接口都必須從lUnknow繼承過來,其原因在于lUnknow接口提供了非常重要的特性:生存期控制和接口查詢??蛻舫绦蛑荒芡ㄟ^接口和COM對(duì)象進(jìn)行通信,雖然客戶程序可以不管對(duì)象內(nèi)部的實(shí)現(xiàn)細(xì)節(jié),但它要控制對(duì)象的存在與否。1.5.1 Queryinterface 接口方法介紹按照CO
22、M規(guī)范,一個(gè)COM對(duì)象可以實(shí)現(xiàn)多個(gè)接口, 客戶端程序可以在運(yùn)行時(shí)刻對(duì)COM對(duì)象的接口進(jìn)行詢問,如果對(duì)象實(shí)現(xiàn)了該接口,則對(duì)象可以提供這樣的接口服務(wù)。QueryInterface函數(shù)的說明:HRESULT QueryI nterface( in REFIID iid, ou void* ppV);函數(shù)的輸入?yún)?shù)iid為接口標(biāo)識(shí)符IID,輸出參數(shù)ppv為查詢得到的結(jié)果接口指針,如果沒有實(shí)現(xiàn)iid所標(biāo)識(shí)的接口,則輸出參數(shù) ppv指向空(NULL)。函數(shù)的返回值為一個(gè) 32位的整數(shù),反映了查詢的結(jié)果,其含義有三種情況:1) S_OK查到了指定的接口,接口指針存放在ppv輸出參數(shù)中;2) E_NOINTE
23、RFACE對(duì)象不支持所指定的接口,*ppv為NULL;3) E_UNEXPECTE,發(fā)生了意外錯(cuò)誤,*ppv 為 NULL。對(duì)于調(diào)用QueryInterface函數(shù),COM規(guī)范給出了以下一些規(guī)則:1) 對(duì)于同一個(gè)對(duì)象的不同接口指針,查詢得到的IUnknown接口必須完全相同。也就是說每個(gè)COM對(duì)象的IUnknown接口指針是唯一的。2) 接口自反性。對(duì)于一個(gè)接口查詢其自身總應(yīng)該成功3) 接口對(duì)稱性。如果從一個(gè)接口指針查詢到另一個(gè)接口指針,則從第二個(gè)接口指 針再回到第一個(gè)接口指針必定成功,如:IMyMath* pIMyMath = NULL;HRESULT hr = :CoCreate In s
24、ta nce( CLSID_MyMath, NULL,CLSCTX_INPROC_SERVER, IID_IMyMath, ( void*)&pIMyMath );IMyMath2* plMyMath2 = NULL;hr = plMyMath-Queryl nterface(IID_IMyMath2,( void* )&plMyMath2);4) 接口傳遞性。如果從第一個(gè)接口指針查詢到第二個(gè)接口指針,從第二個(gè)接口指 針可以查詢到第三個(gè)接口指針,則從第三個(gè)接口指針一定可以查詢到第一個(gè)接 口指針。5) 接口查詢時(shí)間無關(guān)性。如果某一個(gè)時(shí)刻可以查詢到某一個(gè)接口指針,則以后任 何時(shí)候再查詢到同樣的接口
25、指針,一定可以查詢成功。1.5.2 引用計(jì)數(shù)IUnknown 引入了“引用計(jì)數(shù)” ( reference counting )方法,可以有效地控制對(duì)象的生存 周期,解決內(nèi)存管理的問題。 COM 對(duì)象通過引用計(jì)數(shù)來決定是否繼續(xù)生存下去。 IUnknown 的接口成員函數(shù) AddRef和Release分別完成引用計(jì)數(shù)的增1和減1操作。如果一個(gè)COM對(duì)象實(shí)現(xiàn)了多個(gè)接口,則可以采用同樣的計(jì)數(shù)技術(shù),只要引用計(jì)數(shù)不為0,就表明該 COM 對(duì)象的客戶仍然在使用它(前提是客戶程序正確地操作了引用計(jì)數(shù)) ,它 就繼續(xù)生存下去;反之,如果引用計(jì)數(shù)減到 0,則表明客戶不再使用該對(duì)象了,于是它就可 以被清除。需要調(diào)用
26、AddRef方法的情形:1) 當(dāng)把一個(gè)非空指針寫到局部變量中時(shí)2) 當(dāng)被調(diào)用方把一個(gè)非空接口指針寫到方法或者函數(shù)的out或者in,out參數(shù)中時(shí)3) 當(dāng)被調(diào)用方返回一個(gè)非空接口指針作為函數(shù)的實(shí)際結(jié)果時(shí)4) 當(dāng)把一個(gè)非空接口指針寫到對(duì)象的一個(gè)數(shù)據(jù)成員中時(shí)5) 注意:QueryInterface內(nèi)含AddRef,不需要再調(diào)用需要調(diào)用Release方法的情形:1) 在改寫一個(gè)非空局部變量或者數(shù)據(jù)成員之前2) 在離開非局部變量的作用域(scope)之前3) 在被調(diào)用方要改寫方法或者函數(shù)的 in,out 參數(shù),并且參數(shù)的初始值為非空時(shí)。 注意, 對(duì)于傳入的 out 參數(shù),不需要釋放4) 在改寫一個(gè)對(duì)象的
27、非空數(shù)據(jù)成員之前5) 在離開一個(gè)對(duì)象的析構(gòu)函數(shù)之前,并且這時(shí)還有一個(gè)非空接口指針作為數(shù)據(jù)成員特殊情況1) 當(dāng)調(diào)用方把一個(gè)非空接口指針通過i n參數(shù)傳遞給一個(gè)函數(shù)或者方法時(shí),既不需要調(diào)用AddRef,也不需要調(diào)用 Release,因?yàn)樵谡{(diào)用堆棧中,臨時(shí)變量的生命周期只 是“用于初始化形式參數(shù)”的表達(dá)式的生命周期的一個(gè)子集。1.6 重要概念:套間什么是套間?根據(jù)COM技術(shù)內(nèi)幕的觀點(diǎn),COM沒有定義自己新的線程模型,而是 直接利用了 Win32 線程,或者說對(duì)其做了改造、包裝。COM 本質(zhì)論是這樣定義的:套間定義了一組對(duì)象的邏輯組合,這些對(duì)象共享一組并 發(fā)性和沖入限制。每個(gè) COM 對(duì)象都屬于某一個(gè)
28、套間,一個(gè)套間可以包含多個(gè) COM 對(duì)象。MSDN 上解釋說, 可以把進(jìn)程中的組件對(duì)象想象為分成了很多組, 每一組就是一個(gè)套間。 屬于這個(gè)套間的線程, 可以直接調(diào)用組件, 不屬于這個(gè)套間的線程, 要通過代理才能調(diào)用組 件。最直接的說, COM 庫為了實(shí)現(xiàn)簡(jiǎn)化多線程編程的構(gòu)想,提出了套間的概念。套間是一 個(gè)邏輯上的概念,它把 Win32 里的線程、組件等,按照一定的規(guī)則結(jié)合在一起,并且以此 提供一種模式,用于多線程并發(fā)訪問 COM 組件??梢园烟组g看做 COM 對(duì)象的管理者,它 通過調(diào)度,切換 COM對(duì)象的執(zhí)行環(huán)境,保證 COM對(duì)象的多線程調(diào)用正常運(yùn)行。COM和線程不是包含關(guān)系,而是對(duì)應(yīng)和關(guān)聯(lián)關(guān)
29、系。61單進(jìn)程套間STASingle-threaded Apartments, 個(gè)套間只關(guān)聯(lián)一個(gè)線程,COM庫保證對(duì)象只能由這個(gè)線程訪問(通過對(duì)象的接口指針調(diào)用其他方法),其他線程不得直接訪問這個(gè)對(duì)象(可以間接訪問,但最終還是由這個(gè)線程訪問)。COM庫實(shí)現(xiàn)了所有調(diào)用的同步,因?yàn)橹挥嘘P(guān)聯(lián)線程訪問 COM對(duì)象。如果有N個(gè)調(diào)用同時(shí)并發(fā),N-1個(gè)調(diào)用處于阻塞狀態(tài),如圖2。對(duì)象的狀態(tài)(也就是對(duì)象的成員變量的值)肯定是正確變化的,不會(huì)出現(xiàn)線程訪問沖突而導(dǎo)致對(duì)象狀態(tài)錯(cuò)誤。 STA0-Message Queue圖2 STA實(shí)現(xiàn)過程/創(chuàng)建一個(gè)STA套間并和當(dāng)前線程關(guān)聯(lián):Col nitialize(NULL);/
30、或者:CoI ni tializeEx(NULL,COINIT_APARTMENTTHREADED);:CoU nin itialize();上述代碼創(chuàng)建了一個(gè) STA然后套間把當(dāng)前的線程和自己關(guān)聯(lián)在一起,線程被標(biāo)記為套 間線程,只有這個(gè)線程能直接調(diào)用COM對(duì)象。62多進(jìn)程套間MTAMultithreaded Apartments,個(gè)套間可以對(duì)應(yīng)多個(gè)線程,COM對(duì)象可以被多個(gè)線程并發(fā)訪問,如圖3。所以這個(gè)對(duì)象的作者必須在自己的代碼中實(shí)現(xiàn)線程保護(hù)、同步工作,保證 可以正確改變自己的狀態(tài)。圖3 MTA實(shí)現(xiàn)過程/創(chuàng)建一個(gè)MTA套間并和當(dāng)前線程關(guān)聯(lián):Col nitializeEx(NULL,COINI
31、T_COINIT_MULTITHREADED);:CoU nin itialize();第一次如此調(diào)用的時(shí)候,會(huì)創(chuàng)建一個(gè)MTA,然后套間把當(dāng)前線程和自己關(guān)聯(lián)在一起,線程被標(biāo)記為自由線程。以后第二個(gè)線程再調(diào)用(同一個(gè)進(jìn)程中)的時(shí)候,這個(gè)MTA會(huì)把第二個(gè)線程也關(guān)聯(lián)在一起。一個(gè)MTA可以關(guān)聯(lián)多個(gè)線程。所有的關(guān)聯(lián)線程都可以調(diào)用套間中的組件。這就涉及到同步問題,需要組件編寫者解決。這個(gè)對(duì)于作為業(yè)務(wù)邏輯組件或干后臺(tái)服務(wù)的組件非常適合。因?yàn)樽鳛橐粋€(gè)分布式的服務(wù)器,同一時(shí)間可能有幾個(gè)服務(wù)請(qǐng)求到達(dá),如果排隊(duì)進(jìn)行調(diào)用,那么將是不能想象的。63 NA套間COM+為了進(jìn)一步簡(jiǎn)化多線程編程,引入的中立線程套間概念。NA
32、/TNA/NAT, Neutral Apartment/ Thread Neutral Apartment / Neutral Threaded Apartment。這種套間只和對(duì)象相關(guān)聯(lián),沒有關(guān)聯(lián)的線程,因此任何線程都可以直接訪問里 面的對(duì)象,不存在 STA還是MTA。 1.7線程模型屬性組件編寫者可以實(shí)現(xiàn):同一個(gè)組件,既可以在STA中運(yùn)行,也可以在 MTA中運(yùn)行,還可以在兩種環(huán)境中同時(shí)存在。 可以說組件有一種屬性說明可以在哪種環(huán)境中生存,屬性名叫做“線程模型”。這個(gè)屬性為ThreadModel,可以取以下值:1. Main Thread Apartme nt(Si ngle)2. Si n
33、gle Thread Apartme nt(Apartme nt)3. Free Thread Apartme nt(Free)4. Any Apartment(Both)5. Neutral Apartme nt(N/A)根據(jù)ATL項(xiàng)目向?qū)?chuàng)建 COM服務(wù)器時(shí),當(dāng)我們?yōu)镃OM組件添加一個(gè) COM類時(shí)就可以Click here for unsupportied Smart Device Options PreviousFinEhCancel確定其線程模型。NamesOptionsFile T沖e: OptionsOptions;Threadingi mods): r詆逅 t* Apartmen
34、t 廠 Both 廠 Free 廠 N&ytral廠 AutornatiDn compatibleAggregation:肯Yes廠NoC OnlySupport:I EupportErrorlnFoI Connection joints:廠 Free-threaded rnarshalBr廠 lObjsctWithSite (IE object support)選擇原則是根據(jù)組件的功能選擇:如果組件做I/O,首選是Free,因?yàn)榭梢韵鄳?yīng)其他客戶端調(diào)用。如果組件和用戶交互, 首選Apartment,保持消息依次調(diào)用。COM+首選N/A,如果沒有定義,COM庫默認(rèn)為是 MainTread Apa
35、rtment。Apartment簡(jiǎn)單,F(xiàn)ree強(qiáng)大但要自己實(shí)現(xiàn)同步。當(dāng)是進(jìn)程內(nèi)組件時(shí),根據(jù)注冊(cè)表項(xiàng)InprocServer32ThreadingModel和線程的不同,列于下表:創(chuàng)建線程關(guān)聯(lián)的套間種類ThreadingModel 鍵值組件對(duì)象最后所在套間STAApartme nt創(chuàng)建線程的套間STAFree進(jìn)程內(nèi)的MTA套間STABoth創(chuàng)建線程的套間STA或 Single進(jìn)程內(nèi)的主STA套間STANeutral進(jìn)程內(nèi)的NA套間MTAApartme nt新建的一個(gè)STA套間MTAFree進(jìn)程內(nèi)的MTA套間MTABoth進(jìn)程內(nèi)的MTA套間MTA或 Single進(jìn)程內(nèi)的主STA套間MTANeutr
36、al進(jìn)程內(nèi)的NA套間進(jìn)程內(nèi)的主STA套間是進(jìn)程中第一個(gè)調(diào)用CoInitialize的線程所有關(guān)聯(lián)的套間(即進(jìn)程中的第一個(gè)STA套間)。當(dāng)時(shí)進(jìn)程外組件時(shí),由主函數(shù)調(diào)用CoIntializeEx或CoIntialize指定組件所在套間,與上表相同。第2章 COM 服務(wù)器2.1 COM 服務(wù)器類型介紹進(jìn)程內(nèi) COM 組件在Windows中一個(gè)正在被執(zhí)行的程序被稱作是一個(gè)進(jìn)程。每一個(gè)應(yīng)用程序(EXE都將以一個(gè)單獨(dú)的進(jìn)程運(yùn)行,每一個(gè)進(jìn)程都有一個(gè) 4GB 的地址空間。一個(gè)進(jìn)程中的一個(gè)地址同 另一個(gè)進(jìn)程中的某個(gè)地址是不同的。 由于進(jìn)程內(nèi)組件駐留在客戶端程序的地址空間中, 因此 它們共享同一進(jìn)程, 它們也可以
37、共享同一地址空間。 當(dāng)客戶端程序得到組件的一個(gè)接口指針 時(shí),連接客戶和組件的唯一中介是接口的二進(jìn)制結(jié)構(gòu)。 當(dāng)客戶查詢組件的某個(gè)接口時(shí), 它所 請(qǐng)求的實(shí)際上是具體特定格式的一塊內(nèi)存。 當(dāng)組件返回一個(gè)接口指針時(shí), 他告訴客戶的實(shí)際 上是此塊內(nèi)存的地址。 由于接口是在客戶和組件都能夠訪問的內(nèi)存中, 因此這種情況實(shí)際上 與當(dāng)客戶和組件在同一 EXE文件中時(shí)是相同的。進(jìn)程外 COM 服務(wù)器不同EXE中的組件和客戶將在不同的進(jìn)程中運(yùn)行,客戶和組件之間的交互就會(huì)跨越進(jìn)程邊界。在某些情況下,進(jìn)程外 COM 服務(wù)器也可以分為“本地服務(wù)器”和“遠(yuǎn)程服務(wù)器” 。遠(yuǎn) 程服務(wù)器指的是運(yùn)行于另外一個(gè)不用的機(jī)器上的進(jìn)程外服
38、務(wù)器。對(duì)于進(jìn)程間的通信, 有幾種不同的方式,如動(dòng)態(tài)數(shù)據(jù)交換( DDE)、命名管道以及共享內(nèi)存等。遠(yuǎn)程COM服務(wù)器所用的方法是遠(yuǎn)程過程調(diào)用(RPQ,本地服務(wù)器則是通過本地過程調(diào)用(LPC。LPC是同一機(jī)器上不同進(jìn)程間通信的一種方法,它是基于遠(yuǎn)程過程調(diào)用(RPC的用于單機(jī)上進(jìn)程間通信的專利技術(shù)。將函數(shù)調(diào)用的參數(shù)從一個(gè)進(jìn)程的地址空間傳到另外一個(gè)進(jìn)程的地址空間中需要一種 方法“調(diào)整” 。若兩個(gè)進(jìn)程都在同一個(gè)機(jī)器上,則調(diào)整過程是直接的:只需將參數(shù)數(shù)據(jù) 從一個(gè)進(jìn)程的地址空間復(fù)制到另外一個(gè)進(jìn)程的地址空間中。 若參與參數(shù)傳遞的兩個(gè)進(jìn)程在不 同的地址空間中, 那么考慮到不同機(jī)器在數(shù)據(jù)表示方面的不同, 如整數(shù)的字
39、節(jié)順序可能會(huì)不 一樣,必須將參數(shù)數(shù)據(jù)轉(zhuǎn)換成標(biāo)準(zhǔn)的格式。 2.2 代理和存根當(dāng)調(diào)用 Win32函數(shù)時(shí),系統(tǒng)實(shí)現(xiàn)上將調(diào)用一個(gè) DLL中的函數(shù),而此函數(shù)將通過 LPC調(diào) 用 Windows 中的實(shí)際代碼。這種結(jié)構(gòu)可以將用戶進(jìn)程同 Windows 代碼隔離開。由于不同的 進(jìn)程具有不用的地址空間,因此用戶不可能對(duì)操作系統(tǒng)造成破壞。COM使用的結(jié)構(gòu)與此類似??蛻魧⑼粋€(gè)模仿組件的DLL進(jìn)行通信。這個(gè) DLL可以為客戶完成參數(shù)的調(diào)整及 LPC調(diào)用。在COM中,此DLL(也是一個(gè)組件)被稱作是一個(gè)代理。 用COM術(shù)語來說,一個(gè)代理就是同另一個(gè)組件行為相同的組件。代理必須是DLL形式的,因?yàn)樗鼈冃枰L問客戶進(jìn)程
40、的地址空間以便對(duì)傳給接口函數(shù)的數(shù)據(jù)進(jìn)行調(diào)整客戶程序和進(jìn)程外 COM 服務(wù)器之間的調(diào)用關(guān)系如圖 4 所示??蛻舫绦蛑慌c同一進(jìn)程中 的代理(proxy)對(duì)象打交道,組件程序只與同一進(jìn)程中的存根DLL打交道,LPC調(diào)用只在代理對(duì)象和存根DLL之間進(jìn)行。組件還需要一個(gè)被稱作是殘根的DLL,以對(duì)從客戶傳來的數(shù)據(jù)進(jìn)行反調(diào)整,殘根 DLL也將對(duì)傳回給客戶的數(shù)據(jù)進(jìn)行調(diào)整。圖4客戶端與組件調(diào)用關(guān)系遠(yuǎn)程COM服務(wù)器時(shí),即組件程序運(yùn)行在不同的機(jī)器上,則代理DLL和存根DLL就通過RPC方式進(jìn)行網(wǎng)絡(luò)上的過程調(diào)用, 從而實(shí)現(xiàn)分布式組件對(duì)象模型。 因此RPC比LPC更為復(fù)雜, LPC相當(dāng)于一個(gè)優(yōu)化了的 RPC實(shí)現(xiàn)。 2.
41、3列集與散集代理DLL和存根DLL除了完成LPC調(diào)用之外,它還需要對(duì)參數(shù)和返回值進(jìn)行翻譯和傳遞, 客戶程序調(diào)用的參數(shù),首先經(jīng)過代理 DLL的處理,它把參數(shù)以及其他的一些調(diào)用信息組裝成一個(gè)數(shù)據(jù)包傳遞給組件進(jìn)程,這個(gè)過程稱為參數(shù)列集 (marshaling);組件進(jìn)程接收到數(shù)據(jù)包之后,要進(jìn)行解包操作,把參數(shù)信息提取出來,這個(gè)過程被稱為散集(unmarshaling )。函數(shù)的返回值和輸出參數(shù)在返回的過程中也要進(jìn)行列集和散集操作,只是在存根DLL一端進(jìn)行列集,在代理DLL一端進(jìn)行散集,最后把散集后的結(jié)果返回給客戶程序。列集過程可通過兩種方式實(shí)現(xiàn):第一種列集方法為自定義列集法(custom marsh
42、aling)也稱為基本列集法(basic marshaling architecture),其列集過程完全由對(duì)象自身控制,對(duì)象 指定其代理對(duì)象的 CLSID代理對(duì)象控制了其所有接口的列集過程,包括接口參數(shù)的列集和散集,以及代理對(duì)象和存根代碼之間的跨進(jìn)程通信過程。第二種列集方法為標(biāo)準(zhǔn)列集法( standard marshaling),這是由 COM提供缺省的代理 對(duì)象和存根代碼,因?yàn)榱屑^程涉及到操作系統(tǒng)的一些復(fù)雜特性的編程,如共享內(nèi)存操作或其他跨進(jìn)程數(shù)據(jù)傳輸機(jī)制,甚至通過網(wǎng)絡(luò)協(xié)議傳輸數(shù)據(jù),所以COM提供了缺省的代理和存根代碼以及一套標(biāo)準(zhǔn)的列集方法, 可以處理常用數(shù)據(jù)類型的列集和散集, 包括指針
43、類型和接 口指針類型。標(biāo)準(zhǔn)列集法的原理以及其列集過程與自定義列集法完全一致,事實(shí)上標(biāo)準(zhǔn)列集法就是自定義列集法的一個(gè)特例,但兩者有一個(gè)基本的不同:自定義列集法其列集過程完全由對(duì)象自身控制,所以自定義列集法以整個(gè)對(duì)象為列集單位,即對(duì)象指定的代理對(duì)象和存根代碼必須處理對(duì)象支持的所有接口;而標(biāo)準(zhǔn)列集法使用COM提供的標(biāo)準(zhǔn)代理對(duì)象和存根代碼,實(shí)際上該代理對(duì)象和存根代碼只是接口列集過程的管理器,因此,標(biāo)準(zhǔn)列集法是以接口為列集單位,COM提供的很多標(biāo)準(zhǔn)接口,其列集過程已經(jīng)由COM庫提供了,程序員只需要提供自定義接口的列集代碼即可。標(biāo)準(zhǔn)列集的優(yōu)點(diǎn):組件無需實(shí)現(xiàn)IMarshal接口及代理組件,但組件需要為自己生
44、成一個(gè) 代理/存根組件(Proxy/Stub),由于可通過 MIDL由IDL文件自動(dòng)生成,效率高,代碼正確性 有保證。除非有一些特殊目的,否則一般采用標(biāo)準(zhǔn)列集法。下面著重介紹標(biāo)準(zhǔn)列集法。用標(biāo)準(zhǔn)的列集散集函數(shù)列集函數(shù):HRESULT CoMarshalI nterface(in IStream *pStm,寫列集狀態(tài)的位置in REFIID riid,列集指針類型-in ,iid_is(riid) IUnknown *pItf,/列集指針in DWORD dwDestCtx,目標(biāo)的MSHCTXin void *pvDestCtx,/保留必須為0in DWORD dwMshlFlags);標(biāo)志常規(guī)
45、還是表格列集散集函數(shù):/讀取列集狀態(tài)/散集指針類型a存放散集指針的位置HRESULT CoUnm arshall nteface(in IStream *pStm,in REFIID riid,out ,iid_is(riid) void *ppv, );用列集輔助函數(shù)和散集輔助函數(shù)列集輔助函數(shù):在原有基礎(chǔ)上進(jìn)行了簡(jiǎn)單封裝,冋樣適用于冋一進(jìn)程內(nèi)的線程間傳遞接口指針HRESULT CoMarshalI nterThreadl ntefacel nStream(in REFIID riid,in ,iid_is(riid) IUnknown *pItf,out IStream *ppStm);散集
46、輔助函數(shù):在原有基礎(chǔ)上進(jìn)行了簡(jiǎn)單封裝,同樣適用于同一進(jìn)程內(nèi)的線程間傳遞接口指針HRESULT CoGetI ntefaceA ndReleaseStream(in IStream *pStm,/讀取列集狀態(tài)in REFIID riid,/散集指針類型aout ,iid_is(riid) void *ppv, 存放散集指針的位置);全局接口表(GIT, Global Interface Table):允許接口指針被進(jìn)程內(nèi)所有套件訪問。一個(gè) 進(jìn)程擁有一個(gè)GIT其所包含的經(jīng)過列集的接口指針,在同一進(jìn)程中可以被有效的散集多次IGoballntefaceTable 接口定義:MIDL_INTERFACE
47、( 00000146-0000-0000-C000-000000000046)IGIoball nterfaceTable : public IUnknownpublic:virtual HRESULT STDMETHODCALLTYPE RegisterI nterfacel nGlobal(/* annotationin */_in IUnknown *pU nk,/* annotationin */_in REFIID riid,/* anno tatio nout */_out DWORD *pdwCookie) = 0;virtual HRESULT STDMETHODCALLTYP
48、E RevokeI nterfaceFromGlobal(/* annotationin */_in DWORD dwCookie) = 0;virtual HRESULT STDMETHODCALLTYPE Get In terfaceFromGlobal(/* annotationin */_in DWORD dwCookie,/* annotationin */_in REFIID riid,/* anno tatio niid_isout */_deref_out void *ppv) = 0;;下面結(jié)合實(shí)例介紹列集散集函數(shù)方法:實(shí)例工程PrintServer中已實(shí)現(xiàn)一個(gè)本地 COM服
49、務(wù)器,其中暴露的IMyFun接口中包含了兩個(gè)方法:PringOne和PrintTwo。這兩個(gè)方法只是簡(jiǎn)單返回了兩個(gè)不同的字符串,方便 在客戶端調(diào)用時(shí)區(qū)分。/ PrintServer.idl : PrintServer 的 IDL 文件object,uuid(7EC1667C-1037-48D4-ACF0-F1159414793B),poin ter_default(u ni que)in terface IMyFu n : IUnknownid(1), helpstring(方法 PrintOne) HRESULT PrintOne( out, string BSTR *pbstr);id(2
50、), helpstring(方法 PrintTwo) HRESULT PrintTwo( out, string BSTR *pbstr); ;uuid(C606F70F-EF78-47DD-A051-3C93CC093E1D),versio n(1.0),library Prin tServerLibimportlib (stdole2.tlb);uuid(FCD6C50B-EFEB-4E9D-9714-09D5A5341D43)coclassMyFu ndefault in terface IMyFu n;下面介紹客戶端中實(shí)現(xiàn)跨套間調(diào)用接口方法的實(shí)例。 首先獲取接口 COM對(duì)象實(shí)例。/生成
51、MTA套間并關(guān)聯(lián)HRESULT hr = Col nitializeEx(NULL, COINIT_MULTITHREADED); if( FAILED(hr)printf(CoinitializeEx failed!);CloseHa ndle(g_heve ntWritte n); return 0;創(chuàng)建實(shí)例對(duì)象IMyFun * pTest = NULL;hr = :CoCreateI nsta nce(CLSID_MyFu n,0,CLSCTX_LOCAL_SERVER,IIDM yFu n,(void *)&pTest);if( FAILED(hr)printf( CoCreateIn
52、staanee failed!);CloseHa ndle(g_heve ntWritte n);:CoU nini tialize(); return 0;列集實(shí)現(xiàn)函數(shù):/列集實(shí)現(xiàn)函數(shù)HRESULT WritePtrToGlobalVariable(IMyFun *pRacer, HGLOBAL & rhglobal ) /存放列集指針I(yè)Stream *pStmPtr = NULL;/分配并封裝內(nèi)存塊HRESULT hr = CreateStream OnH Global(0,FALSE,&pStmPtr);if( SUCCEEDED(hr)將列集對(duì)象寫入內(nèi)存塊hr = CoMarshalI
53、 nterface( pStmPtr,IID_IMyFu n, pRacer,MSHCTX_INPROC ,0,MSHLFLAGS_TABLESTRONG );抽岀底層內(nèi)存的句柄if( SUCCEEDED(hr)hr = GetHGlobalFromStream( pStmPtr, & g_rhglobal); pStmPtr-Release();SetEve nt(g_heve ntWritte n);return hr;散集實(shí)現(xiàn)函數(shù):/散集函數(shù)HRESULT ReadPtrFromGlobalVariable(HGLOBAL rhglobal,IMyFun *&rpRacer)IStream *pStmPtr = NULL;/現(xiàn)有內(nèi)存封裝塊從輸入傳入HRESULT hr = CreateStream OnH Global( g_rhglobal, FALSE, & pStmPtr );if( SUCCEEDED(hr)獲取對(duì)象有效指針hr = CoUnmarshalInterface( pStmPtr, IID_IMyFun, ( void*)&rpRacer); pStmPtr-Release();return hr;在實(shí)現(xiàn)函數(shù)中,先調(diào)用列集函數(shù),然后在創(chuàng)建的線程執(zhí)行函數(shù)中,就可
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 小學(xué)五年綜合發(fā)展規(guī)劃(2020.9-2025.8)
- 菱形網(wǎng)格護(hù)坡施工方案
- 2024年渤海理工職業(yè)學(xué)院高職單招職業(yè)適應(yīng)性測(cè)試歷年參考題庫含答案解析
- 醫(yī)院會(huì)計(jì)核算和財(cái)務(wù)管理相關(guān)問題探討培訓(xùn)講學(xué)
- 二零二五年環(huán)保設(shè)施建設(shè)合同作廢聲明模板3篇
- 6年級(jí)英語上滬教版
- Module3Unit9DinnerisreadyPeriod1(課件)-滬教牛津版(深圳用)英語二年級(jí)上冊(cè)
- (完整版)監(jiān)控?cái)z像頭安裝安全技術(shù)交底
- 東南大學(xué)-區(qū)域經(jīng)濟(jì)學(xué)課件(2013-9-21)
- 2025版4A級(jí)旅游景區(qū)門票銷售合作協(xié)議3篇
- 2024-2025學(xué)年高考英語語法第一輪復(fù)習(xí):定語從句(講義)(原卷版+解析)
- DB35T 2082-2022 人民防空疏散基地建設(shè)基本要求
- 瑞士萬通831KF卡爾費(fèi)休水分測(cè)定儀干貨-庫侖法
- 第14章第1節(jié)熱機(jī)-課件(共21張課件)-人教版初中物理九年級(jí)全一冊(cè).課件
- 四年級(jí)數(shù)學(xué)(上)計(jì)算題專項(xiàng)練習(xí)及答案匯編
- 廣東省廣州市(2024年-2025年小學(xué)四年級(jí)語文)人教版期末考試(上學(xué)期)試卷及答案
- 23-燃?xì)赓|(zhì)量檢測(cè)制度
- 《火災(zāi)調(diào)查 第2版》 課件全套 劉玲 第1-12章 緒論、詢問 -火災(zāi)物證鑒定
- 借用他人名義買車協(xié)議完整版
- 校園超市經(jīng)營(yíng)投標(biāo)方案(技術(shù)方案)
- 基于Web服務(wù)的辦公系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)的開題報(bào)告
評(píng)論
0/150
提交評(píng)論