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