什么是com接口
按照前文所說(shuō)的,你不用去釋放COM對(duì)象,需要做的僅僅是告訴它們你使用完了。每個(gè)COM對(duì)象實(shí)現(xiàn)的IUnknown接口都有一個(gè)Release()方法。你應(yīng)該調(diào)用這個(gè)方法通知COM對(duì)象你不再需要它了。一旦調(diào)用了Release(),COM對(duì)象就從內(nèi)存中消失,因此也就不能再使用接口指針了。
如果你的應(yīng)用程序使用許多不同的COM對(duì)象,那么當(dāng)你使用完接口之后調(diào)用Release()就顯得極為重要。如果你不釋放(release)接口,COM對(duì)象(還有包括代碼的那些DLLs)將被保存在內(nèi)存中,并且毫無(wú)必要的加入到你的應(yīng)用程序中。如果應(yīng)用程序要運(yùn)行很長(zhǎng)一段時(shí)間,在程序閑時(shí),你應(yīng)該調(diào)用CoFreeUnusedLibraries()函數(shù)。這個(gè)函數(shù)將卸載沒(méi)有顯著作用的COM服務(wù)器,這也能減少應(yīng)用程序的內(nèi)存使用量。
繼續(xù)上面的示例,下面展示應(yīng)該如何使用Release():
// Create COM object as above.? Then...
??? if ( SUCCEEDED ( hr ) )
??????? ...{
??????? // Call methods using pISL here.
??????? // Tell the COM object that we're done with it.
??????? pISL->Release();
??????? }
IUnknown接口將在下一部分詳細(xì)說(shuō)明。
基本接口 - IUnknown
每個(gè)COM接口都是從IUnknown繼承而來(lái)。這個(gè)名字有點(diǎn)容易讓人誤解,因?yàn)閷?shí)際它并不是一個(gè)未知(unknown)接口。這個(gè)名字意味著即使你有了一個(gè)指向COM對(duì)象的IUnknown指針,你也不會(huì)知道它下面的對(duì)象是什么,因?yàn)槊總€(gè)COM對(duì)象都實(shí)現(xiàn)了IUnknown。
IUnknown 有三個(gè)方法:
1.? AddRef() - 告知COM對(duì)象增加它的引用計(jì)數(shù)。如果你拷貝了一個(gè)接口指針,你就需要使用這個(gè)方法,無(wú)論原始指針還是拷貝的副本都需要使用。在本文中,我們不必使用AddRef()方法。
2. Release() - 告知COM對(duì)象減少它的引用計(jì)數(shù)。你可以從前面的代碼片段中找到關(guān)于Release()的說(shuō)明。
3. QueryInterface() - 從COM對(duì)象中獲取一個(gè)接口指針。當(dāng)COClass實(shí)現(xiàn)二個(gè)或二個(gè)以上接口的時(shí)候,需要使用這個(gè)方法。
我們已經(jīng)了解Release()是怎樣運(yùn)作的,那么QueryInterface()又是怎樣的呢?當(dāng)你用CoCreateInstance()創(chuàng)建一個(gè)COM對(duì)象的時(shí)候,你將得到一個(gè)接口指針。如果COM對(duì)象實(shí)現(xiàn)了二個(gè)或二個(gè)以上的接口(不包括IUnknown),你可以使用QueryInterface()來(lái)獲取任意你想要的額外指針。QueryInterface()的原型如下:
HRESULT IUnknown::QueryInterface (
??? REFIID iid,
??? void** ppv );
參數(shù)如下:
iid
??? 所請(qǐng)求接口的IID
ppv
??? 接口指針的地址。如果調(diào)用成功,QueryInterface()則通過(guò)這個(gè)參數(shù)返回接口。
讓我們繼續(xù)那個(gè)快捷方式的的示例。生成快捷方式的COClass實(shí)現(xiàn)了IShellLink和IPersistFile接口。如果你已經(jīng)有個(gè)一個(gè)IShellLink指針pISL,那面你可以像下面一樣來(lái)從COM對(duì)象中獲取IPersistFIle接口:
HRESULT hr;
IPersistFile* pIPF;
??? hr = pISL->QueryInterface ( IID_IPersistFile, (void**) &pIPF );
之后你可以用SUCCEEDED宏測(cè)試hr,然后證明QueryInterface()是否成功運(yùn)行了。如果成功,你就可以像使用其它接口一樣使用新的接口指針pIPF了。當(dāng)然,在你使用完畢后,一定要調(diào)用pIPF->Release()。
在討論COM以前,我們得認(rèn)識(shí)到一個(gè)事實(shí),編寫軟件實(shí)際上是一個(gè)非常耗費(fèi)時(shí)間和金錢的活動(dòng),所以人們不斷尋找方法以減少這些花費(fèi),一個(gè)很重要的就是“軟件重用”。在一個(gè)理想的環(huán)境下,我們應(yīng)該能夠編寫一次代碼,在任何地方都可以運(yùn)行,即使這個(gè)環(huán)境編寫者都沒(méi)有想到過(guò)。當(dāng)一個(gè)程序員修改了自己發(fā)布給別人使用的函數(shù)功能后,使用者應(yīng)該不需要改變或者重新編譯程序就可以使用這個(gè)功能。
早期的努力是使用類庫(kù),這個(gè)工作在C++中比較常見,但是這種做法是有很大缺陷的,要共享C++的二進(jìn)制代碼是非常困難的。為了解決這個(gè)問(wèn)題,程序員們?cè)噲D建立一種標(biāo)準(zhǔn)去達(dá)到軟件在二進(jìn)制級(jí)別上的共用。
Components Object Model (COM) 是軟件組件互相通訊的一種方式。它是一種二進(jìn)制和網(wǎng)絡(luò)標(biāo)準(zhǔn),允許任意兩個(gè)組件互相通訊,而不管它們是在什么計(jì)算機(jī)上運(yùn)行(只要計(jì)算機(jī)是相連的),不管各計(jì)算機(jī)運(yùn)行的是什么操作系統(tǒng)(只要該系統(tǒng)支持 COM),也不管該組件是用什么語(yǔ)言編寫的。COM 還提供了位置透明性:當(dāng)您編寫組件時(shí),其他組件是進(jìn)程內(nèi) DLL、本地 EXE 還是位于其他計(jì)算機(jī)上的組件,對(duì)您而言都無(wú)所謂。COM 是基于對(duì)象的——但是這種對(duì)象概念與您熟悉的 C++ 或 Visual Basic 中的對(duì)象不太一樣。
首先,COM 對(duì)象被很好地封裝起來(lái)。您無(wú)法訪問(wèn)對(duì)象的內(nèi)部實(shí)現(xiàn)細(xì)節(jié);您無(wú)法知道對(duì)象使用了什么數(shù)據(jù)結(jié)構(gòu)。實(shí)際上,對(duì)象的封裝是如此的嚴(yán)密,以致于 COM 對(duì)象通常被描繪為盒子。細(xì)節(jié)是不會(huì)告訴你的,但是我們可以通過(guò)接口來(lái)訪問(wèn)COM對(duì)象里面的方法,當(dāng)然如果COM組件提供商肯告訴你那些接口中的函數(shù)和屬性起什么作用的話。
??? 概括地說(shuō),COM具有如下一些優(yōu)越性:
?? 編程技術(shù)難度和工作量降低,開發(fā)周期變短,開發(fā)成本降低。一般編程人員只須根據(jù)應(yīng)用功能要求選用合適的組件,而不必事無(wú)巨細(xì)都自己動(dòng)手去完成。組件模塊將編程的技術(shù)難度和工作量在人員個(gè)體和時(shí)間上進(jìn)行了分?jǐn)偂N覀兪褂肊SRI的COM組件編寫程序就屬于這一級(jí)別。
?? 實(shí)現(xiàn)分層次的編程,從而促進(jìn)了軟件的專業(yè)化生產(chǎn)。專業(yè)人員可以開發(fā)出具有很強(qiáng)專業(yè)性的軟件組件,這樣既保證了普通的編程應(yīng)用人員能夠完成所需要的應(yīng)用開發(fā),又不至于降低使用的性能。應(yīng)用人員不便實(shí)現(xiàn)的組件模塊可以讓專業(yè)人員定做。ESRI的程序員們使用C語(yǔ)言為我們辨析了一個(gè)個(gè)COM組件給我們使用。
?? 軟件的復(fù)用率提高,使軟件的使用效率得到提高并延長(zhǎng)了使用壽命。組件編程體系使大量的編程問(wèn)題局部化了,使軟件的更新和維護(hù)變得快速和容易,軟件的成本大大降低。新的函數(shù)功能如果在接口沒(méi)有改變的情況下很容易使用。
談了這么多COM的好處,我們?cè)撝v點(diǎn)技術(shù)型的東西了,我們?cè)贏O編程中需要那些COM知識(shí)呢?
1. COM不是接口,也不是對(duì)象,它是一種標(biāo)準(zhǔn)。
2. 符合COM標(biāo)準(zhǔn)的對(duì)象就是我們要談?wù)摰闹攸c(diǎn)——COM對(duì)象。其實(shí)COM對(duì)象也無(wú)非是實(shí)現(xiàn)了很多接口的對(duì)象而已。
3. COM對(duì)象必須實(shí)現(xiàn)Iunknown接口,這個(gè)接口是管理COM對(duì)象生命周期的,當(dāng)COM對(duì)象不使用的時(shí)候,是這個(gè)接口定義的方法負(fù)責(zé)釋放內(nèi)存。一個(gè)COM對(duì)象可以沒(méi)有任何別的接口,但是這個(gè)必須要,它是默認(rèn)實(shí)現(xiàn)的接口。
4. QI,即所謂查詢接口。由于COM對(duì)象有很多個(gè)接口,不同的接口管理著COM的不同類型的方法,因此從一個(gè)接口可以使用的方法轉(zhuǎn)到另一個(gè)接口可以使用的方法的過(guò)程稱為QI,這個(gè)過(guò)程是由Idispatch接口管理的。
5. GUIDs 每個(gè)組件都有一個(gè)獨(dú)一無(wú)二的標(biāo)識(shí),這就是所謂的廣泛唯一標(biāo)識(shí)符。這個(gè)標(biāo)識(shí)符就是COM組件的身份,它是一個(gè)128bits的數(shù)字,由系統(tǒng)自由分配,不要擔(dān)心這個(gè)標(biāo)識(shí)會(huì)有重復(fù)的一天。如果我們每秒產(chǎn)生1000萬(wàn)個(gè)UID,那么到5770年才可能遇到重復(fù)。別告訴我那個(gè)時(shí)候我們還使用WINDOWS的玩意。
6. 一個(gè)COM對(duì)象可以有多個(gè)接口,一個(gè)接口也完全可以被多個(gè)COM對(duì)象實(shí)現(xiàn)。
7. 接口分為兩種,內(nèi)置接口和外置接口。前一種定義的是COM對(duì)象的方法和屬性,用implements實(shí)現(xiàn),COM對(duì)象必須實(shí)現(xiàn)所有的接口內(nèi)容;后一種定義的是COM對(duì)象的事件,用withEvents實(shí)現(xiàn),這種接口在實(shí)現(xiàn)的時(shí)候不必實(shí)現(xiàn)所有的內(nèi)容。
8. COM組件必須被注冊(cè)后才能使用,它得到注冊(cè)表那里去登記“戶口”。
COM組件很不錯(cuò),可是它也有致命的缺陷,這個(gè)缺陷就來(lái)自它本身。我們知道,COM是可以被重用的,COM對(duì)象的實(shí)現(xiàn)過(guò)程也可以被修改升級(jí)(定義是不能修改的哦),如果兩個(gè)程序都使用一個(gè)COM對(duì)象,而這個(gè)COM組件升級(jí)了的話,很可能就出現(xiàn)某個(gè)程序無(wú)法使用新組件的情況,這就被稱為“DLL HELL(DLL災(zāi)難)”,我們有時(shí)候安裝了新軟件后很多別的軟件都無(wú)法使用,很多原因就是因?yàn)檫@個(gè)DLL HELL。別以為這是個(gè)小問(wèn)題,這可是人家微軟提出.NET平臺(tái)的一個(gè)主要原因。
評(píng)論
查看更多