vs2019 Com組件初探-簡(jiǎn)單的COM編寫以及實(shí)現(xiàn)跨語(yǔ)言調(diào)用 上一篇實(shí)現(xiàn)了如何編寫基于IDispatch接口的COM以及vbs如何調(diào)用編寫的COM 本次主要是實(shí)現(xiàn)VBS的CreateObject函數(shù)的邏輯,也就是在不知道類名的情況下如何調(diào)用基于IDispathc接口的COM 前提條件 1、掌握C++基礎(chǔ)語(yǔ)法 2、平臺(tái)安裝 vs2019 3、本地平臺(tái)為 windows 10 1909 X64 4、基本的DLL編程知識(shí) (不是必備) 本次目標(biāo) 1、創(chuàng)建DLL并實(shí)現(xiàn)CreateObject函數(shù) 2、寫一個(gè)調(diào)用DLL的demo 1、創(chuàng)建DLL并實(shí)現(xiàn)CreateObject函數(shù) 首先通過(guò)VS創(chuàng)建一個(gè) 動(dòng)態(tài)鏈接庫(kù) 在編寫之前先梳理程序的執(zhí)行流程 初始化 Com庫(kù) 獲取函數(shù)指針 傳入?yún)?shù) 調(diào)用函數(shù)指針 卸載Com庫(kù) 接下來(lái)就開(kāi)始寫我們的DLL vs2019 創(chuàng)建DLL項(xiàng)目后系統(tǒng)會(huì)默認(rèn)多出來(lái)頭文件 以及源文件 我們打開(kāi)pch.h頭文件定義我們的函數(shù)聲明 參數(shù)為 COM組件progID,函數(shù)名,參數(shù)數(shù)量,變長(zhǎng)參數(shù) extern "C" 以C的方式定義 _declspec(dllimport) 定義此函數(shù)為要導(dǎo)出的函數(shù) 新建一個(gè)ComInit.h 定義Com庫(kù)的初始化和卸載庫(kù)函數(shù) 1 // ComInit.h 2 3 #pragma once 4 static bool _init = false; 5 6 // 初始化 7 bool Init(); 8 9 // 結(jié)束初始化10 void Release(); 新建一個(gè)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; } } 之后打開(kāi)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 // 報(bào)錯(cuò)宏 7 #define ASSERT(s) if((s) == true) 8 9 // Com類名,函數(shù)名,傳入的參數(shù)數(shù)量,變長(zhǎng)參數(shù)10 VARIANT CreateObject(const WCHAR* __comname,const WCHAR* __funcname,int __count, ...)11 {12 /* Com注冊(cè)到系統(tǒng)后使用 */13 14 // 是否成功初始化15 if (true == Init())16 {17 // ProgId值存放18 CLSID clsid;19 20 // 通過(guò) ProgID 取得組件的 CLSID21 // CLSID 值存放在注冊(cè)表 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 * 用來(lái)接收指向Com對(duì)象接口地址的指針變量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 // 通過(guò)IUnknown智能指針,聲明新的IDispatch智能指針45 CComDispatchDriver spDisp(spUnk);46 47 // 參數(shù)數(shù)組48 VARIANT* __args = new VARIANT[__count];49 50 // 變長(zhǎng)參數(shù)變量51 va_list ap;52 53 // 定位到第一個(gè)函數(shù)變長(zhǎng)參數(shù)54 va_start(ap, __count);55 56 // 循環(huán)獲取變長(zhǎng)參數(shù),并轉(zhuǎn)換為 VARIANT 類型放入 __args變量57 for (auto i = 0; i < __count; i++)58 __args[i] = va_arg(ap, VARIANT);59 60 // 結(jié)束變長(zhǎng)參數(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庫(kù)84 Release();85 86 // 返回值87 return __ret;88 }89 90 assert(_init == false);91 } 完成后編譯(CTRL+B)獲取到新的dll和lib文件(x64)以及項(xiàng)目的pch.h頭文件 2、寫一個(gè)調(diào)用DLL的demo vs2019 新建基于 控制臺(tái)程序 的項(xiàng)目 移動(dòng)dll和lib以及pch.h文件到新建項(xiàng)目目錄下,并對(duì)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 對(duì)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)錯(cuò)誤,檢查調(diào)用的Com是否已經(jīng)注冊(cè) 如何注冊(cè)我在上一篇里面有講過(guò) 接下來(lái)修改代碼嘗試調(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)命令,打開(kāi)了一個(gè)記事本 注意事項(xiàng): com基于IDispatch 接口才可以調(diào)用 Com必須已經(jīng)注冊(cè)到系統(tǒng) (小心誤刪或者移動(dòng)路徑) 卸載DLL函數(shù)為 regsvr32.exe -ui [DLL未知] DLL對(duì)應(yīng)版本盡量一致 github源碼: |
|