vs2019 Com組件初探-簡單的COM編寫以及實現跨語言調用html
上一篇實現瞭如何編寫基於IDipatch接口的COM以及vbs如何調用編寫的COMios
本次主要是實現VBS的CreateObject函數的邏輯git
前提條件github
一、掌握C++基礎語法shell
二、平臺安裝 vs2019編程
三、本地平臺爲 windows 10 1909 X64windows
四、基本的DLL編程知識 (不是必備)api
本次目標數組
一、建立DLL並實現CreateObject函數函數
二、寫一個調用DLL的demo
一、建立DLL並實現CreateObject函數
首先經過VS建立一個 動態連接庫
在編寫以前先梳理程序的執行流程
初始化 Com庫
獲取函數指針
傳入參數
調用函數指針
卸載Com庫
接下來就開始寫咱們的DLL
vs2019 建立DLL項目後系統會默認多出來頭文件
以及源文件
咱們打開pch.h頭文件定義咱們的函數聲明
參數爲 COM組件progID,函數名,參數數量,變長參數
extern "C" 以C的方式定義
_declspec(dllimport) 定義此函數爲要導出的函數
新建一個ComInit.h 定義Com庫的初始化和卸載庫函數
1 // ComInit.h 2 3 #pragma once 4 static bool _init = false; 5 6 // 初始化 7 bool Init(); 8 9 // 結束初始化 10 void Release();
新建一個ComInit.cpp 實現Init和Release函數
// ComInit.cpp #include "pch.h" #include "ComInit.h" bool Init() { if (_init == true) { return _init; } else { if (S_OK == CoInitialize(NULL)) _init = true; else _init = false; return _init; } return false; } void Release() { if (true == _init) { CoUninitialize(); _init = false; } }
以後打開pch.cpp實現CreateObject函數
1 #include "pch.h" 2 #include "ComStart.h" 3 #include <assert.h> 4 #include <atlbase.h> 5 6 // 報錯宏 7 #define ASSERT(s) if((s) == true) 8 9 // Com類名,函數名,傳入的參數數量,變長參數 10 VARIANT CreateObject(const WCHAR* __comname,const WCHAR* __funcname,int __count, ...) 11 { 12 /* Com註冊到系統後使用 */ 13 14 // 是否成功初始化 15 if (true == Init()) 16 { 17 // ProgId值存放 18 CLSID clsid; 19 20 // 經過 ProgID 取得組件的 CLSID 21 // CLSID 值存放在註冊表 HKEY_CLASSES_ROOT [以__comname加.1爲鍵值(MyCom.FirstClass.1)] 22 HRESULT hr = ::CLSIDFromProgID(__comname, &clsid); 23 24 ASSERT(S_OK != hr) 25 assert(hr != S_OK); 26 27 // 智能指針獲取 IUnknow 28 CComPtr<IUnknown>spUnk; 29 30 /* 31 * CoCreateInstance 32 * CLSIDFromProgId獲取的值 33 * 指向接口IUnknown的指針 34 * 運行可執行代碼的上下文[CLSCTX_ALL 爲全部] 35 * IID_IUnknown爲返回類型 36 * 用來接收指向Com對象接口地址的指針變量 37 */ 38 // 獲取IUnknow內容 39 hr = ::CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_IUnknown, (LPVOID*)&spUnk); 40 41 ASSERT(S_OK != hr) 42 assert(hr != S_OK); 43 44 // 經過IUnknown智能指針,聲明新的IDispatch智能指針 45 CComDispatchDriver spDisp(spUnk); 46 47 // 參數數組 48 VARIANT* __args = new VARIANT[__count]; 49 50 // 變長參數變量 51 va_list ap; 52 53 // 定位到第一個函數變長參數 54 va_start(ap, __count); 55 56 // 循環獲取變長參數,並轉換爲 VARIANT 類型放入 __args變量 57 for (auto i = 0; i < __count; i++) 58 __args[i] = va_arg(ap, VARIANT); 59 60 // 結束變長參數 61 va_end(ap); 62 63 // Com函數返回值存放 64 VARIANT __ret; 65 66 // 執行Com函數 67 68 /* 69 * [InvokeN] 70 * 函數名 71 * 函數參數 72 * 函數數量 73 * 返回值存放處 74 */ 75 hr = spDisp.InvokeN((LPCOLESTR)__funcname, __args, __count, &__ret); 76 77 ASSERT(S_OK != hr) 78 assert(hr != S_OK); 79 80 // 內存回收 81 delete[] __args; 82 83 // 卸載 Com庫 84 Release(); 85 86 // 返回值 87 return __ret; 88 } 89 90 assert(_init == false); 91 }
完成後編譯(CTRL+B)獲取到新的dll和lib文件(x64)以及項目的pch.h頭文件
二、寫一個調用DLL的demo
vs2019 新建基於 控制檯程序 的項目
移動dll和lib以及pch.h文件到新建項目目錄下,並對pch.h文件添加代碼
// pch.h #pragma once #include <combaseapi.h> // 新添加的代碼 #pragma comment(lib,"ComPack.lib") extern "C" _declspec(dllimport) VARIANT CreateObject(const WCHAR * __comname, const WCHAR * __funcname, int __count, ...);
找到main函數 寫入調用代碼
#include <iostream> #include "pch.h" int main() { // 參數類型必須爲VARIANT VARIANT __param1; // 參數類型爲 LONG __param1.vt = VT_I4; // 參數值爲 2 __param1.lVal = 2; // 獲取ComTest.Temp並調用Number 函數 參數數量爲1 對Number函數傳入參數__param1 VARIANT __ret = CreateObject(L"ComTest.Temp", L"Number",1, __param1); std::cout << __ret.lVal << std::endl; }
執行並運行顯示執行結果
運行出現錯誤,檢查調用的Com是否已經註冊
如何註冊我在上一篇裏面有講過
接下來修改代碼嘗試調用Wscript.shell裏面的Run函數
#include <iostream> #include "pch.h" int main() { VARIANT __param1; // 參數類型爲BSTR __param1.vt = VT_BSTR; // 建立BSTR格式的字符串 __param1.bstrVal = SysAllocString(L"notepad.exe"); // 調用函數並釋放BSTR VARIANT __ret = CreateObject(L"Wscript.shell", L"run",1, __param1); SysFreeString(__param1.bstrVal); }
值得一提的是 COM組件的字符串和以往的字符串有所不一樣,建立方式和銷燬方式也不一樣
SysAllocString爲建立BSTR字符串
SysFreeString 爲釋放BSTR字符串
運行結果能夠看到已經成功的執行了系統命令,打開了一個記事本
注意事項:
com基於IDispatch 接口才能夠調用
Com必須已經註冊到系統 (當心誤刪或者移動路徑)
卸載DLL函數爲 regsvr32.exe -ui [DLL未知]
DLL對應版本儘可能一致
github源碼: