COM学习(三)——COM的跨语言
COM是基于二進(jìn)制的組件模塊,從設(shè)計(jì)之初就以支持所有語(yǔ)言作為它的一個(gè)目標(biāo),這篇文章主要探討COM的跨語(yǔ)言部分。
idl文件
一般COM接口的實(shí)現(xiàn)肯定是以某一具體語(yǔ)言來(lái)實(shí)現(xiàn)的,比如說(shuō)使用VC++語(yǔ)言,這就造成了一個(gè)問(wèn)題,不同的語(yǔ)言對(duì)于接口的定義,各個(gè)變量的定義各不相同,如何讓使用vc++或者說(shuō)Java等其他語(yǔ)言定義的接口能被別的語(yǔ)言識(shí)別?為了達(dá)到這個(gè)要求,定義了一種文件格式idl——(Interface Definition Language)接口定義語(yǔ)言,IDL提供一套通用的數(shù)據(jù)類(lèi)型,并以這些數(shù)據(jù)類(lèi)型來(lái)定義更為復(fù)雜的數(shù) 據(jù)類(lèi)型。一般來(lái)說(shuō),一個(gè)文件有下面幾個(gè)部分說(shuō)明
實(shí)現(xiàn)類(lèi)的定義
而各個(gè)部分又包括他們的屬性定義,以及函數(shù)成員的定義
屬性:
屬性是在接口定義的上方,使用“[]”符號(hào)包裹,一般在屬性中使用下面幾個(gè)關(guān)鍵字:
object:標(biāo)明該部分是一個(gè)對(duì)象(可以理解為c++中的對(duì)象,包括接口和具體的實(shí)現(xiàn)類(lèi))
uuid:標(biāo)明該部分的GUID
version:該部分的版本
接口定義
接口定義采用關(guān)鍵字interface,接口函數(shù)定義在一對(duì)大括號(hào)中,它的定義與類(lèi)的定義相似,其中函數(shù)定義需要修飾函數(shù)各個(gè)參數(shù)的作用,比如使用in 表示它作為輸入?yún)?shù),out表示作為輸出參數(shù),retval表示該參數(shù)作為返回值,一般在VC++定義的接口中,函數(shù)返回值為HRESULT,但是需要返回一個(gè)值供外界調(diào)用,此時(shí)就使用輸出參數(shù)并加上retval表示它將在其他語(yǔ)言中作為函數(shù)的返回值。
組件庫(kù)定義
庫(kù)使用library關(guān)鍵字定義,在定義庫(kù)的時(shí)候,它的屬性一般定義GUID和版本信息,而在庫(kù)中通常定義庫(kù)中的實(shí)現(xiàn)類(lèi)的相關(guān)信息,庫(kù)中的信息也是寫(xiě)在一對(duì)大括號(hào)中
實(shí)現(xiàn)類(lèi)的定義
接口實(shí)現(xiàn)類(lèi)使用關(guān)鍵字coclass,接口類(lèi)的屬性一般定義一個(gè)object,一個(gè)GUID,然后一般定義實(shí)現(xiàn)類(lèi)不需要向在C++中那樣定義它的各個(gè)接口,各個(gè)數(shù)據(jù)成員,只需要告知它實(shí)現(xiàn)哪些接口即可,也就是說(shuō)它繼承自哪些接口。下面是一個(gè)具體的例子:
上面的例子中定義了一個(gè)IMyString接口繼承自IUnknown接口,函數(shù)參數(shù)列表中in表示參數(shù)為輸入?yún)?shù),out表示它為輸出參數(shù),retval表示該參數(shù)是函數(shù)的返回值。import導(dǎo)入了一個(gè)庫(kù)文件類(lèi)似于include。而importlib導(dǎo)入一個(gè)tlb文件,我們可以將其看成VC++中的#pragma comment導(dǎo)入一個(gè)lib庫(kù)
從上面不難看出一個(gè)IDL文件至少有3個(gè)ID,一個(gè)是接口ID,一個(gè)是庫(kù)ID,還有一個(gè)就是實(shí)現(xiàn)類(lèi)的ID
在VC環(huán)境中通過(guò)midl命令可以對(duì)該文件進(jìn)行編譯,編譯會(huì)生成下面幾個(gè)我們?cè)诰帉?xiě)實(shí)現(xiàn)時(shí)會(huì)用到的重要文件:
需要實(shí)現(xiàn)的導(dǎo)出函數(shù)
一般我們需要在dll文件中導(dǎo)出下面幾個(gè)全局的導(dǎo)出函數(shù):
STDAPI DllRegisterServer(void); STDAPI DllUnregisterServer(void); STDAPI DllGetClassObject(const CLSID & rclsid, const IID & riid, void ** ppv); STDAPI DllCanUnloadNow(void);其中DllRegisterServer用來(lái)向注冊(cè)表中注冊(cè)模塊的相關(guān)信息,主要注測(cè)在HKEY_CLASSES_ROOT中,主要定義下面幾項(xiàng)內(nèi)容:
下面是具體的定義:
使用上一篇博文的代碼,來(lái)循環(huán)注冊(cè)這些項(xiàng)即可
DllGetClassObject:該函數(shù)用來(lái)生成對(duì)應(yīng)的工廠(chǎng)類(lèi),而工廠(chǎng)類(lèi)負(fù)責(zé)產(chǎn)生對(duì)應(yīng)接口的實(shí)現(xiàn)類(lèi)。
DllCanUnloadNow:函數(shù)用來(lái)詢(xún)問(wèn)是否可以卸載對(duì)應(yīng)的dll,一般在COM中有兩個(gè)全局的引用計(jì)數(shù),用來(lái)記錄當(dāng)前內(nèi)存中有多少個(gè)模塊中的類(lèi),以及當(dāng)前有多少個(gè)線(xiàn)程在使用它,如果當(dāng)前沒(méi)有線(xiàn)程使用或者存在的對(duì)象數(shù)為0,則可以卸載
實(shí)現(xiàn)類(lèi)的定義
實(shí)現(xiàn)部分的整體結(jié)構(gòu)圖如下:
由于所有類(lèi)都派生自IUnknown,所在在這里就不顯示這個(gè)基類(lèi)了。
每個(gè)實(shí)現(xiàn)類(lèi)都對(duì)應(yīng)了一個(gè)它具體的類(lèi)工廠(chǎng),而項(xiàng)目中CMyString類(lèi)的類(lèi)廠(chǎng)的定義如下:
STDMETHOD宏展開(kāi)如下:
#define STDMETHOD(method) virtual HRESULT __stdcall method所以上面的代碼展開(kāi)后就變成了:
virtual HRESULT __stdcall CreateInstance((IUnknown *pUnkOuter, REFIID riid, void **ppvObject);另外3個(gè)派生自IUnknown接口就沒(méi)什么好說(shuō)的,主要說(shuō)說(shuō)另外兩個(gè):
CreateInstance:主要用來(lái)生成對(duì)應(yīng)的實(shí)現(xiàn)類(lèi),然后再調(diào)用實(shí)現(xiàn)類(lèi)——CMyString的QueryInterface函數(shù)生成對(duì)應(yīng)的接口
LockServer:當(dāng)前是否被鎖住:如果傳入的值為T(mén)RUE,則表示被鎖住,對(duì)應(yīng)的鎖計(jì)數(shù)器+1, 否則 -1
至于CMyString類(lèi)的代碼與之前的大同小異,也就沒(méi)什么說(shuō)的。
其他語(yǔ)言想要調(diào)用,以該項(xiàng)目為例,一般會(huì)經(jīng)歷下面幾個(gè)步驟:
調(diào)用模塊的導(dǎo)出函數(shù)DllGetClassObject將查詢(xún)到的CLSID作為第一個(gè)參數(shù),并將接口ID作為第二個(gè)參數(shù)傳入,得到一個(gè)接口
6.后面根據(jù)idl文件中的定義,直接調(diào)用接口中提供的函數(shù)
真實(shí)ATLCOM項(xiàng)目的解析
最后來(lái)看看一個(gè)正式的ATLCOM項(xiàng)目里面的內(nèi)容,來(lái)復(fù)習(xí)前面的內(nèi)容,首先通過(guò)VC創(chuàng)建一個(gè)ATLCOM的dll項(xiàng)目在項(xiàng)目上右鍵-->New Atl Object,輸入接口名稱(chēng),IDE會(huì)根據(jù)名稱(chēng)生成一個(gè)對(duì)應(yīng)的接口,還是以MyString接口為例,完成這一步后,整個(gè)項(xiàng)目的類(lèi)結(jié)構(gòu)如下:
這些全局函數(shù)的作用與之前的相同,它里面多了一個(gè)_Module的全局對(duì)象,該對(duì)象類(lèi)似于MFC中的CWinApp類(lèi),它用來(lái)表示整個(gè)項(xiàng)目的實(shí)例,里面封裝了對(duì)于引用計(jì)數(shù)的管理,以及對(duì)項(xiàng)目中各個(gè)接口注冊(cè)信息的管理,所以看DllRegisterServer等函數(shù)就會(huì)發(fā)現(xiàn)它們里面其實(shí)很簡(jiǎn)單,大部分的工作都由_Module對(duì)象完成。
整個(gè)IDL文件的定義如下:
里面的內(nèi)容與上一次的內(nèi)容相差無(wú)幾,多了一個(gè)helpstring屬性,該屬性用于產(chǎn)生幫助信息,當(dāng)使用者在調(diào)用接口函數(shù)時(shí)IDE會(huì)將此提示信息顯示給調(diào)用者。
由于有系統(tǒng)框架給我們做的大量的工作,我們?cè)僖膊挥藐P(guān)心像引用計(jì)數(shù)的問(wèn)題,只需要將精力集中在編寫(xiě)接口的實(shí)現(xiàn)上,減少了不必要的工作量。
至此從結(jié)構(gòu)上說(shuō)明了為了實(shí)現(xiàn)跨語(yǔ)言COM組件內(nèi)部做了哪些工作,當(dāng)然只有這些工作是肯定不夠的,后面會(huì)繼續(xù)說(shuō)明它所做的另一塊工作——提供的一堆通用的變量類(lèi)型。
轉(zhuǎn)載于:https://www.cnblogs.com/lanuage/p/7758219.html
總結(jié)
以上是生活随笔為你收集整理的COM学习(三)——COM的跨语言的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: STM32(HAL库 ) AS608光学
- 下一篇: 【3D动态思维导图制作软件】万彩脑图大师