vs2019 Com组件初探-简单的COM编写以及实现跨语言调用

上一篇实现了如何编写基于IDipatch接口的COM以及vbs如何调用编写的COM

本次主要是实现VBS的CreateObject函数的逻辑

前提条件

  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 对Add函数传入参数__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)

vs2019 Com组件初探-通过IDispatch接口调用Com的更多相关文章

  1. vs2019 Com组件初探-简单的COM编写以及实现跨语言调用

    前提条件 1.掌握C++基础语法 2.平台安装 vs2019 3.本地平台为 windows 10 1909 X64 4.了解vbs基础语法 本次目标 1.掌握Com组件的概念及原理 2.编写一个简单 ...

  2. 【转载】COM 组件设计与应用(十)——IDispatch 接口 for VC.NET

    原文:http://vckbase.com/index.php/wv/1225.html 一.前言 终于写到了第十回,我也一直期盼着写这回的内容耶,为啥呢?因为自动化(automation)是非常常用 ...

  3. 【转载】COM 组件设计与应用(九)——IDispatch 接口 for VC6.0

    原文: http://vckbase.com/index.php/wv/1224.html 一.前言 终于写到了第九回,我也一直期盼着写这回的内容耶,为啥呢?因为自动化(automation)是非常常 ...

  4. Python的扩展接口[2] -> 动态链接库DLL[1] -> 组件对象模型 COM 的 Python 调用

    组件对象模型 COM 的 Python 调用 关于COM的基本概念,可参考组件对象模型 COM的内容,下面主要介绍两种使用 Python 调用 COM 组件的方法. 1 使用 win32com 1.1 ...

  5. wpf 错误 执行了 QueryInterface 调用,请求提供 COM 可见的托管类“BoilerMonitoringV1._0.MapControl”的默认 IDispatch 接口。

    在做wpf嵌入地图时,在自定义的WebBrowser 里面使用JavaScript调用外部方法的时报的错误 在原来的WinForm里 我们只要在窗体类设置的头部设置个 [System.Runtime. ...

  6. COM 组件基础——GUID 和 接口

    一.前言 书接上回,话说在 doc(Word) 复合文件中,已经解决了保存 xls(Excel) 数据的问题了.那么,接下来又要解决另一个问题:当 WORD 程序读取复合文件,遇到了 xls 数据的时 ...

  7. IDispatch接口 - GetIDsOfNames和Invoke(转)

    IDispatch接口是COM自动化的核心.其实,IDispatch这个接口本身也很简单,只有4个方法: IDispatch : public IUnknown { public: virtual H ...

  8. IDispatch接口介绍

    1.         C程序调用时,调用者必须预先知道接口规范(如,参数类型.参数字节长度.参数顺序等).由于不同语言这些规范有所不同,COM未解决不同语言之间调用,提供了IDispatch接口. 2 ...

  9. SmartRoute之远程接口调用和负载

    基于接口的调用远比基于基础消息交互来得更简单和便于维护,特别在业务展现上,接口作为业务表现更适合其便利性.为了让SmartRoute更适合业务应用集成,在新的一年开始SmartRoute集成了远程接口 ...

随机推荐

  1. P1948 [USACO08JAN]Telephone Lines S

    题意描述 在无向图中求一条从 \(1\) 到 \(N\) 的路径,使得路径上第 \(K+1\) 大的边权最小. 等等,最大的最小...如此熟悉的字眼,难道是 二分答案. 下面进入正题. 算法分析 没错 ...

  2. 漫谈:从APP崩溃率标准,到Monkey介绍拓展Maxim,及Jenkins自动化配置,持续集成获取崩溃monkey日志

    漫谈:从APP崩溃率标准,到Monkey介绍拓展Maxim,及Jenkins自动化配置,持续集成获取崩溃日志 1.APP崩溃率标准 开发或测试app的同学,对于app崩溃肯定非常熟悉,频繁的线上崩溃属 ...

  3. MySQL全面瓦解7:查询的过滤条件

    概述 在实际的业务场景应用中,我们经常要根据业务条件获取并筛选出我们的目标数据.这个过程我们称之为数据查询的过滤.而过滤过程使用的各种条件(比如日期时间.用户.状态)是我们获取精准数据的必要步骤, 这 ...

  4. Shell 教程01

    Shell 教程 Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言. Shell 是指一种应用程序,这个应用程序提供了一个 ...

  5. java中在构造方法中修改线程名,修改失败原因(现已修改成功)

    package com.cx.Threads; public class HelloThread extends Thread{ @Override public void run() { for ( ...

  6. 80%人会答错的JS基础面试题

    这套题第一道题难度最大,我第一遍的回答居然也错的,我悲观估计80%的JavaScript从业人员都答不完全准确 []==![] 得到什么? false, 你还需要看看基础 true, 恭喜你答对了,你 ...

  7. Vue3教程:用 Vue3 开发小程序,这里有一份实际的代码案例!

    前言 寻寻觅觅冷冷清清,凄凄惨惨戚戚. Vue 3 发布以后,最近也在学习和写一些 Vue3 的 demo 和项目,我也一直想着什么时候能在小程序里使用新特性? 于是我翻遍了市面上的小程序框架,如 u ...

  8. leetcode bitmap系列问题整理

    1. 题目: 编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 '1' 的个数(也被称为汉明重量). 示例 : 输入: 11输出: 3解释: 整数 11 的二进制表示为 000000 ...

  9. select的限制

    /*一.select实现并发服务器并发的两点限制 1.一个进能够打开的最大文件描述符限制.可以通过两种方式修改 ulimit -n :获取最大文件描述符个数 ulimit -n 2048:修改为204 ...

  10. python读取excel数据转换成字典

    以上面的excel格式,输出字典类型: import xlrddef read_excel_data(): filename = 'E:\学历列表.xls' data = xlrd.open_work ...