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