C++ com編程學習詳解
COM簡介
COM
全程為component object model
,是一個二進制標準可以用於跨語言調用dll模塊或者實現組件化以及復用。com不僅可以用在單個操作系統也可以用在跨服務上,在很多大型軟件如wps,office你都會看到它的身影。
比如java中調用規范如下:
JAVA COM編程
你可能會在電腦出現缺少dll情況,一種修復方式下載dll然後調用regsvr32.exe xxx.dll
即可修復。
上面便是COM組件的註冊,本質是把這個dll信息註冊到註冊表中,以便其他系統軟件可以加載。
flutter也提供相關接口封裝flutter相關文檔鏈接
本文主要介紹c++下使用com規范編程。
為什麼需要COM?僅僅是為調用dll何必引用一個如此復雜的概念?
1.假設某個exe升級其中一個dll想要僅發佈dll而不是是發佈主體文件,在大多數情況下是沒有任何問題的。但是在不同編譯器編譯(或者同編譯器不同版本)出的主體exe和dll是有可能出現內存佈局上的差異引起的奔潰。startoverflow上的一個經典問題
2.跨語言調用,比如c語言以\0結束,但是不是所有語言字符串定義都是如此。
3.跨進程或者跨服務上調度dll函數
4.dll代碼復用 與共享
COM 規范
com
使用idl文件去定義dll
函數或者接口,之後用midl編譯器生產對應的頭文件,開發者再利用其去實現接口。
接口有自己的標識符號IID
防止與其他人的接口在名字上沖突.
編譯後的某個頭文件你會看到IID_XXXXX 如下所示
如果說IID是為瞭標識一個接口,那麼應該還有一個ID去用於標識實現類,這個實現類的id我們稱為CLSID,CLSID會在註冊表映射一個dll信息,也就是我們可以用個這個CLSID可以在註冊表中尋找到dll文件信息。
tip:一個實現類可能會包含多個接口
更多idl語法可以參閱官方指南:
https://docs.microsoft.com/en-us/windows/win32/com/defining-com-interfaces
https://bbs.csdn.net/topics/30094944?list=34484
使用ATL編寫一個com共享dll庫 使用管理員權限運行vs(編譯dll會自動調用regsvr32註冊到註冊表,但是需要權限)
首先創建一個ATL工程,創建後你會看到一個idl文件
新建一個接口如下:
上面ProgId一個可選項,它的作用是提供瞭另一種方式尋找註冊過的dll。
完成後我們的IDL會自動產生相關語法到文件中
同時會創建對應的頭文件和c文件如下
此時我們到類視圖添加一個接口方法
添加後idl同樣會如下圖所示生產對應的語法
對應的c文件自行實現接口(最後一個參數作為返回參數)
編譯後會產生 工程名_i.c和工程名.h文件,並且自動會將dll註冊註冊表中。
將上訴兩個文件拷貝其他使用工程中(註意我們並沒有拷貝dll)如下圖所示:
然後再調代碼如下所示調用:
#include <iostream> #include"FMYALTFOUR_i.h" int main() { //初始化 CoInitialize(NULL); IClassFactory *pFactory = NULL; //通過CLSID從註冊表中查到dll位置並加載 然後返回一個類工廠 HRESULT hr = CoGetClassObject(CLSID_IfmyMathHelper,CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void**)&pFactory ); //利用類工廠得到一個接口實例化對象 IIfmyMathHelper * pSuperMath = NULL; pFactory->CreateInstance(NULL, IID_IIfmyMathHelper, (void**)&pSuperMath); long ret; pSuperMath->add(1, 2, &ret); //反初始化 CoUninitialize(); }
當然這是其中一種調用方式,還有一種是預留給vb這類語言調用的實現這種方式你不需要拷貝上訴兩個文件,但是創建接口必須勾選接口雙重。
int main() { //初始化 CoInitialize(NULL); HRESULT hr; GUID clsid; IUnknown FAR* punk; IDispatch FAR* pdisp = (IDispatch FAR*)NULL; //通過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 原理學習
regsvr32.exe xxx.dll
本質作用會加載dll然後調用如下幾個函數,dll應該根據規范在對應函數中實現對應的邏輯(比如DllRegisterServer中應當實現註冊信息到註冊表中)
上面幾個函數在你創建atl工程的def文件可以看到.
我們接下來看看註冊表中的信息,dll首先會利用CLSID
的數值在如下註冊表路徑創建對應的信息
計算機\HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{xxxxxxxxxxx}
如果ProgId會在如下圖位置創建額外的信息,主要用於提供其他方式尋找到dll信息。
其中32位系統和64系統可能路徑有所不同可以參考如下鏈接所示
How to use the Regsvr32 tool and troubleshoot Regsvr32 error messages
自己模擬atl的實現代碼: https://github.com/Zjvngvn/studyCom.git
ActiveX
ActiveX也是基於Com實現的一個UI組件庫。你可以在ATL下輕松的創建對應控件,然後在其他工程插入即可
總結
本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!
推薦閱讀:
- win10360安全瀏覽器打不開是什麼原因 電腦上360安全瀏覽器打不開的解決方法
- 解決docker 容器設置中文語言包出現的問題
- PHP中針對區域語言標記信息的操作
- JavaScript CollectGarbage函數案例詳解
- vue3中的elementPlus全局組件中文轉換方式