C++ com編程學(xué)習(xí)詳解
COM簡(jiǎn)介
COM全程為component object model ,是一個(gè)二進(jìn)制標(biāo)準(zhǔn)可以用于跨語(yǔ)言調(diào)用dll模塊或者實(shí)現(xiàn)組件化以及復(fù)用。com不僅可以用在單個(gè)操作系統(tǒng)也可以用在跨服務(wù)上,在很多大型軟件如wps,office你都會(huì)看到它的身影。
比如java中調(diào)用規(guī)范如下:
你可能會(huì)在電腦出現(xiàn)缺少dll情況,一種修復(fù)方式下載dll然后調(diào)用regsvr32.exe xxx.dll即可修復(fù)。

上面便是COM組件的注冊(cè),本質(zhì)是把這個(gè)dll信息注冊(cè)到注冊(cè)表中,以便其他系統(tǒng)軟件可以加載。
flutter也提供相關(guān)接口封裝flutter相關(guān)文檔鏈接
本文主要介紹c++下使用com規(guī)范編程。
為什么需要COM?僅僅是為調(diào)用dll何必引用一個(gè)如此復(fù)雜的概念?
1.假設(shè)某個(gè)exe升級(jí)其中一個(gè)dll想要僅發(fā)布dll而不是是發(fā)布主體文件,在大多數(shù)情況下是沒(méi)有任何問(wèn)題的。但是在不同編譯器編譯(或者同編譯器不同版本)出的主體exe和dll是有可能出現(xiàn)內(nèi)存布局上的差異引起的奔潰。startoverflow上的一個(gè)經(jīng)典問(wèn)題
2.跨語(yǔ)言調(diào)用,比如c語(yǔ)言以\0結(jié)束,但是不是所有語(yǔ)言字符串定義都是如此。
3.跨進(jìn)程或者跨服務(wù)上調(diào)度dll函數(shù)
4.dll代碼復(fù)用 與共享
COM 規(guī)范
com使用idl文件去定義dll函數(shù)或者接口,之后用midl編譯器生產(chǎn)對(duì)應(yīng)的頭文件,開發(fā)者再利用其去實(shí)現(xiàn)接口。
接口有自己的標(biāo)識(shí)符號(hào)IID 防止與其他人的接口在名字上沖突.

編譯后的某個(gè)頭文件你會(huì)看到IID_XXXXX 如下所示

如果說(shuō)IID是為了標(biāo)識(shí)一個(gè)接口,那么應(yīng)該還有一個(gè)ID去用于標(biāo)識(shí)實(shí)現(xiàn)類,這個(gè)實(shí)現(xiàn)類的id我們稱為CLSID,CLSID會(huì)在注冊(cè)表映射一個(gè)dll信息,也就是我們可以用個(gè)這個(gè)CLSID可以在注冊(cè)表中尋找到dll文件信息。

tip:一個(gè)實(shí)現(xiàn)類可能會(huì)包含多個(gè)接口
更多idl語(yǔ)法可以參閱官方指南:
https://docs.microsoft.com/en-us/windows/win32/com/defining-com-interfaces
https://bbs.csdn.net/topics/30094944?list=34484
使用ATL編寫一個(gè)com共享dll庫(kù) 使用管理員權(quán)限運(yùn)行vs(編譯dll會(huì)自動(dòng)調(diào)用regsvr32注冊(cè)到注冊(cè)表,但是需要權(quán)限)
首先創(chuàng)建一個(gè)ATL工程,創(chuàng)建后你會(huì)看到一個(gè)idl文件

新建一個(gè)接口如下:


上面ProgId一個(gè)可選項(xiàng),它的作用是提供了另一種方式尋找注冊(cè)過(guò)的dll。

完成后我們的IDL會(huì)自動(dòng)產(chǎn)生相關(guān)語(yǔ)法到文件中

同時(shí)會(huì)創(chuàng)建對(duì)應(yīng)的頭文件和c文件如下

此時(shí)我們到類視圖添加一個(gè)接口方法

添加后idl同樣會(huì)如下圖所示生產(chǎn)對(duì)應(yīng)的語(yǔ)法

對(duì)應(yīng)的c文件自行實(shí)現(xiàn)接口(最后一個(gè)參數(shù)作為返回參數(shù))

編譯后會(huì)產(chǎn)生 工程名_i.c和工程名.h文件,并且自動(dòng)會(huì)將dll注冊(cè)注冊(cè)表中。
將上訴兩個(gè)文件拷貝其他使用工程中(注意我們并沒(méi)有拷貝dll)如下圖所示:

然后再調(diào)代碼如下所示調(diào)用:
#include <iostream>
#include"FMYALTFOUR_i.h"
int main()
{
//初始化
CoInitialize(NULL);
IClassFactory *pFactory = NULL;
//通過(guò)CLSID從注冊(cè)表中查到dll位置并加載 然后返回一個(gè)類工廠
HRESULT hr = CoGetClassObject(CLSID_IfmyMathHelper,CLSCTX_INPROC_SERVER,
NULL,
IID_IClassFactory, (void**)&pFactory
);
//利用類工廠得到一個(gè)接口實(shí)例化對(duì)象
IIfmyMathHelper * pSuperMath = NULL;
pFactory->CreateInstance(NULL, IID_IIfmyMathHelper, (void**)&pSuperMath);
long ret;
pSuperMath->add(1, 2, &ret);
//反初始化
CoUninitialize();
}
當(dāng)然這是其中一種調(diào)用方式,還有一種是預(yù)留給vb這類語(yǔ)言調(diào)用的實(shí)現(xiàn)這種方式你不需要拷貝上訴兩個(gè)文件,但是創(chuàng)建接口必須勾選接口雙重。
int main()
{
//初始化
CoInitialize(NULL);
HRESULT hr;
GUID clsid;
IUnknown FAR* punk;
IDispatch FAR* pdisp = (IDispatch FAR*)NULL;
//通過(guò)progId反向查找出clsid 去加載dll
hr = CLSIDFromProgID(OLESTR("progIdfmyMathHelper.1"), &clsid);
IDispatch* pDispatch = NULL;
hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IDispatch, (void**)&pDispatch);
LPOLESTR szMember[1] = { (LPOLESTR)OLESTR("add") };
DISPID dipid[1] = { 0 };
hr=pDispatch->GetIDsOfNames(IID_NULL, szMember, 1, LOCALE_USER_DEFAULT, dipid);
CComVariant vars[2];
DISPPARAMS args = { NULL,NULL,0,0 };
vars[0] = 2;
vars[1] = 1;
args.cArgs = 2;
args.rgvarg = vars;
CComVariant Ret;
hr=pDispatch->Invoke(dipid[0], IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD,
&args, &Ret,NULL,NULL
);
std::cout << "Hello World!\n" << Ret.lVal;
//反初始化
CoUninitialize();
}
COM 原理學(xué)習(xí)
regsvr32.exe xxx.dll 本質(zhì)作用會(huì)加載dll然后調(diào)用如下幾個(gè)函數(shù),dll應(yīng)該根據(jù)規(guī)范在對(duì)應(yīng)函數(shù)中實(shí)現(xiàn)對(duì)應(yīng)的邏輯(比如DllRegisterServer中應(yīng)當(dāng)實(shí)現(xiàn)注冊(cè)信息到注冊(cè)表中)

上面幾個(gè)函數(shù)在你創(chuàng)建atl工程的def文件可以看到.
我們接下來(lái)看看注冊(cè)表中的信息,dll首先會(huì)利用CLSID的數(shù)值在如下注冊(cè)表路徑創(chuàng)建對(duì)應(yīng)的信息
計(jì)算機(jī)\HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{xxxxxxxxxxx}


如果ProgId會(huì)在如下圖位置創(chuàng)建額外的信息,主要用于提供其他方式尋找到dll信息。

其中32位系統(tǒng)和64系統(tǒng)可能路徑有所不同可以參考如下鏈接所示
How to use the Regsvr32 tool and troubleshoot Regsvr32 error messages
自己模擬atl的實(shí)現(xiàn)代碼: https://github.com/Zjvngvn/studyCom.git
ActiveX
ActiveX也是基于Com實(shí)現(xiàn)的一個(gè)UI組件庫(kù)。你可以在ATL下輕松的創(chuàng)建對(duì)應(yīng)控件,然后在其他工程插入即可

總結(jié)
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
C語(yǔ)言代碼實(shí)現(xiàn)猜數(shù)字
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言代碼實(shí)現(xiàn)猜數(shù)字,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11
C語(yǔ)言鏈表實(shí)現(xiàn)通訊錄系統(tǒng)課程設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言鏈表實(shí)現(xiàn)通訊錄系統(tǒng)課程設(shè)計(jì),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
C++實(shí)現(xiàn)簡(jiǎn)單版通訊錄管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)簡(jiǎn)單版通訊錄管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06

