日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

COM学习(三)——COM的跨语言

發(fā)布時(shí)間:2024/3/26 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 COM学习(三)——COM的跨语言 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

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ō)明

  • 接口的定義
  • 組件庫(kù)的定義
  • 實(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è)具體的例子:
  • import "unknwn.idl";[object,uuid(CF809C44-8306-4200-86A1-0BFD5056999E) ] interface IMyString : IUnknown {HRESULT Init([in] BSTR bstrInit);HRESULT GetLength([out, retval] ULONG *pretLength);HRESULT Find([in] BSTR bstrFind, [out, retval] BSTR* bstrSub); };[uuid(ADF50A71-A8DD-4A64-8CCA-FFAEE2EC7ED2),version(1.0) ] library ComDemoLib {importlib("stdole32.tlb");[uuid(EBD699BA-A73C-4851-B721-B384411C99F4)]coclass CMyString{interface IMyString;}; };

    上面的例子中定義了一個(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ì)用到的重要文件:

  • 一個(gè).h文件:包含各個(gè)部分的聲明,以及接口的定義
  • 一個(gè)_i.c文件:包含各個(gè)部分的定義,主要是各個(gè)GUI的定義
  • 需要實(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)容:

  • 字符串名稱(chēng)項(xiàng),該項(xiàng)中包含一個(gè)默認(rèn)值,一般給組件的字符串名稱(chēng);CLSID子健,一般給實(shí)現(xiàn)類(lèi)的GUID;CurVer子健一般是子健的版本
  • 以版本字符串為鍵的注冊(cè)表項(xiàng),該項(xiàng)中主要保存:默認(rèn)值,當(dāng)前版本的項(xiàng)目名稱(chēng);CLSID當(dāng)前版本庫(kù)的實(shí)現(xiàn)類(lèi)的GUID
  • 在HKEY_CLASSES_ROOT/CLSID子健中注冊(cè)以實(shí)現(xiàn)類(lèi)GUID字符串為鍵的注冊(cè)表項(xiàng),里面主要包含:默認(rèn)值,組件字符串名稱(chēng);InprocServer32,組件所在模塊的全路徑;ProgID組件名稱(chēng);TypeLib組件類(lèi)型庫(kù)的ID,也就是在定義IDL文件時(shí),定義的實(shí)現(xiàn)庫(kù)的GUID。
    下面是具體的定義:
  • const TCHAR *g_RegTable[][3] = {{ _T("CLSID\\{EBD699BA-A73C-4851-B721-B384411C99F4}"), 0, _T("FirstComLib.MyString")}, //組件ID{ _T("CLSID\\{EBD699BA-A73C-4851-B721-B384411C99F4}\\InprocServer32"), 0, (const TCHAR*)-1 }, //組建路徑{ _T("CLSID\\{EBD699BA-A73C-4851-B721-B384411C99F4}\\ProgID"), 0, _T("FirstComLib.MyString")}, //組件名稱(chēng){ _T("CLSID\\{EBD699BA-A73C-4851-B721-B384411C99F4}\\TypeLib"), 0, _T("{ADF50A71-A8DD-4A64-8CCA-FFAEE2EC7ED2}") }, //類(lèi)型庫(kù)ID{ _T("FirstComLib.MyString"), 0, _T("FirstComLib.MyString") }, //組件的字符串名稱(chēng){ _T("FirstComLib.MyString\\CLSID"), 0, _T("{EBD699BA-A73C-4851-B721-B384411C99F4}")}, //組件的CLSID{ _T("FirstComLib.MyString\\CurVer"), 0, _T("FirstComLib.MyString.1.0") }, //組件版本{ _T("FirstComLib.MyString.1.0"), 0, _T("FirstComLib.MyString") }, //當(dāng)前版本的項(xiàng)目名稱(chēng){ _T("FirstComLib.MyString.1.0\\CLSID"), 0, _T("{EBD699BA-A73C-4851-B721-B384411C99F4}")} //當(dāng)前版本的CLSID };

    使用上一篇博文的代碼,來(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)的定義如下:

    class CMyClassFactory : public IClassFactory { public:CMyClassFactory();~CMyClassFactory();STDMETHOD(CreateInstance)(IUnknown *pUnkOuter, REFIID riid, void **ppvObject);STDMETHOD(LockServer)(BOOL isLock);STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject);STDMETHOD_(ULONG, AddRef)(void);STDMETHOD_(ULONG, Release)(void);protected:ULONG m_refs; };

    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)用對(duì)應(yīng)語(yǔ)言提供的產(chǎn)生接口的函數(shù),該函數(shù)參數(shù)一般是傳入一個(gè)組件的字符串名稱(chēng)。如果要引用該項(xiàng)目中的組件則會(huì)傳入FirstComLib.MyString
  • 在注冊(cè)表的HKEY_CLASSES_ROOT\組件字符串名\CLSID(比如HKEY_CLASSES_ROOT\FirstComLib.MyString\CLSID)中找到對(duì)應(yīng)的CLSID值
  • 在HKEY_CLASSES_ROOT\CLSID\對(duì)應(yīng)ID\InprocServer32(CLSID\{EBD699BA-A73C-4851-B721-B384411C99F4}\InprocServer32)位置處找到對(duì)應(yīng)模塊的路徑
  • 加載該模塊
  • 根據(jù)IDL文件告知其他語(yǔ)言里面存在的接口,由語(yǔ)言調(diào)用對(duì)應(yīng)的創(chuàng)建接口的函數(shù)創(chuàng)建接口
  • 調(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文件的定義如下:
  • import "oaidl.idl"; import "ocidl.idl";[object,uuid(E3BD0C14-4D0C-48F2-8702-9F8DBC96E154),dual,helpstring("IMyString Interface"),pointer_default(unique)]interface IMyString : IDispatch{};[uuid(A61AC54A-1B3D-4D8E-A679-00A89E2CBE93),version(1.0),helpstring("FirstAtlCom 1.0 Type Library") ] library FIRSTATLCOMLib {importlib("stdole32.tlb");importlib("stdole2.tlb");[uuid(11CBC0BE-B2B7-4B5C-A186-3C30C08A7736),helpstring("MyString Class")]coclass MyString{[default] interface IMyString;}; };

    里面的內(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)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。