vs2019 Com組件初探-簡單的COM編寫以及實(shí)現(xiàn)跨語言調(diào)用 上一篇實(shí)現(xiàn)了如何編寫基于IDispatch接口的COM以及vbs如何調(diào)用編寫的COM 本次主要是實(shí)現(xiàn)VBS的CreateObject函數(shù)的邏輯,也就是在不知道類名的情況下如何調(diào)用基于IDispathc接口的COM 前提條件 1、掌握C++基礎(chǔ)語法 2、平臺安裝 vs2019 3、本地平臺為 windows 10 1909 X64 4、基本的DLL編程知識 (不是必備) 本次目標(biāo) 1、創(chuàng)建DLL并實(shí)現(xiàn)CreateObject函數(shù) 2、寫一個調(diào)用DLL的demo 1、創(chuàng)建DLL并實(shí)現(xiàn)CreateObject函數(shù) 首先通過VS創(chuàng)建一個 動態(tài)鏈接庫 在編寫之前先梳理程序的執(zhí)行流程 初始化 Com庫 獲取函數(shù)指針 傳入?yún)?shù) 調(diào)用函數(shù)指針 卸載Com庫 接下來就開始寫我們的DLL vs2019 創(chuàng)建DLL項(xiàng)目后系統(tǒng)會默認(rèn)多出來頭文件 以及源文件 我們打開pch.h頭文件定義我們的函數(shù)聲明 參數(shù)為 COM組件progID,函數(shù)名,參數(shù)數(shù)量,變長參數(shù) extern "C" 以C的方式定義 _declspec(dllimport) 定義此函數(shù)為要導(dǎo)出的函數(shù) 新建一個ComInit.h 定義Com庫的初始化和卸載庫函數(shù) 1 // ComInit.h 2 3 #pragma once 4 static bool _init = false; 5 6 // 初始化 7 bool Init(); 8 9 // 結(jié)束初始化10 void Release(); 新建一個ComInit.cpp 實(shí)現(xiàn)Init和Release函數(shù) // 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實(shí)現(xiàn)CreateObject函數(shù) 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類名,函數(shù)名,傳入的參數(shù)數(shù)量,變長參數(shù)10 VARIANT CreateObject(const WCHAR* __comname,const WCHAR* __funcname,int __count, ...)11 {12 /* Com注冊到系統(tǒng)后使用 */13 14 // 是否成功初始化15 if (true == Init())16 {17 // ProgId值存放18 CLSID clsid;19 20 // 通過 ProgID 取得組件的 CLSID21 // 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 // 智能指針獲取 IUnknow28 CComPtr<IUnknown>spUnk;29 30 /* 31 * CoCreateInstance32 * CLSIDFromProgId獲取的值33 * 指向接口IUnknown的指針34 * 運(yùn)行可執(zhí)行代碼的上下文[CLSCTX_ALL 為所有]35 * IID_IUnknown為返回類型36 * 用來接收指向Com對象接口地址的指針變量37 */38 // 獲取IUnknow內(nèi)容 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 // 參數(shù)數(shù)組48 VARIANT* __args = new VARIANT[__count];49 50 // 變長參數(shù)變量51 va_list ap;52 53 // 定位到第一個函數(shù)變長參數(shù)54 va_start(ap, __count);55 56 // 循環(huán)獲取變長參數(shù),并轉(zhuǎn)換為 VARIANT 類型放入 __args變量57 for (auto i = 0; i < __count; i++)58 __args[i] = va_arg(ap, VARIANT);59 60 // 結(jié)束變長參數(shù)61 va_end(ap);62 63 // Com函數(shù)返回值存放64 VARIANT __ret;65 66 // 執(zhí)行Com函數(shù)67 68 /*69 * [InvokeN]70 * 函數(shù)名71 * 函數(shù)參數(shù)72 * 函數(shù)數(shù)量73 * 返回值存放處74 */75 hr = spDisp.InvokeN((LPCOLESTR)__funcname, __args, __count, &__ret);76 77 ASSERT(S_OK != hr)78 assert(hr != S_OK);79 80 // 內(nèi)存回收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)以及項(xiàng)目的pch.h頭文件 2、寫一個調(diào)用DLL的demo vs2019 新建基于 控制臺程序 的項(xiàng)目 移動dll和lib以及pch.h文件到新建項(xiàng)目目錄下,并對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函數(shù) 寫入調(diào)用代碼 #include <iostream>#include "pch.h"int main() {// 參數(shù)類型必須為VARIANT VARIANT __param1;// 參數(shù)類型為 LONG__param1.vt = VT_I4;// 參數(shù)值為 2__param1.lVal = 2;// 獲取ComTest.Temp并調(diào)用Number 函數(shù) 參數(shù)數(shù)量為1 對Number函數(shù)傳入?yún)?shù)__param1VARIANT __ret = CreateObject(L"ComTest.Temp", L"Number",1, __param1); std::cout << __ret.lVal << std::endl; } 執(zhí)行并運(yùn)行顯示執(zhí)行結(jié)果 運(yùn)行出現(xiàn)錯誤,檢查調(diào)用的Com是否已經(jīng)注冊 如何注冊我在上一篇里面有講過 接下來修改代碼嘗試調(diào)用Wscript.shell里面的Run函數(shù) #include <iostream>#include "pch.h"int main() { VARIANT __param1;// 參數(shù)類型為BSTR__param1.vt = VT_BSTR;// 創(chuàng)建BSTR格式的字符串__param1.bstrVal = SysAllocString(L"notepad.exe");// 調(diào)用函數(shù)并釋放BSTRVARIANT __ret = CreateObject(L"Wscript.shell", L"run",1, __param1); SysFreeString(__param1.bstrVal); } 值得一提的是 COM組件的字符串和以往的字符串有所不同,創(chuàng)建方式和銷毀方式也不同 SysAllocString為創(chuàng)建BSTR字符串 SysFreeString 為釋放BSTR字符串 運(yùn)行結(jié)果可以看到已經(jīng)成功的執(zhí)行了系統(tǒng)命令,打開了一個記事本 注意事項(xiàng): com基于IDispatch 接口才可以調(diào)用 Com必須已經(jīng)注冊到系統(tǒng) (小心誤刪或者移動路徑) 卸載DLL函數(shù)為 regsvr32.exe -ui [DLL未知] DLL對應(yīng)版本盡量一致 github源碼: |
|