vs2019 Com組件初探-簡單的COM撰寫以及實作跨語言呼叫
上一篇實作了如何撰寫基于IDispatch介面的COM以及vbs如何呼叫撰寫的COM
本次主要是實作VBS的CreateObject函式的邏輯,也就是在不知道類名的情況下如何呼叫基于IDispathc介面的COM
前提條件
1、掌握C++基礎語法
2、平臺安裝 vs2019
3、本地平臺為 windows 10 1909 X64
4、基本的DLL編程知識 (不是必備)
本次目標
1、創建DLL并實作CreateObject函式
2、寫一個呼叫DLL的demo
1、創建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頭檔案


2、寫一個呼叫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原始碼:
3065190005/ComTest: ComTest Code (github.com)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/228341.html
標籤:C++
