QT 开发COM(ActiveX)组件基础介绍和方案验证

news/2024/5/17 16:34:14

一、COM简介

1.1 COM是什么?

COM,Component Object Model,即组件对象模型,是一种以组件为发布单元的对象模型,这种模型使各软件组件可以用一种统一的方式进行交互。COM 既提供了组件之间进行交互的规范,也提供了实现交互的环境,因为组件对象之间交互的规范不依赖于任何特定的语言,所以,COM也可以是不同语言协作开发的一种标准。

COM实际上是一种组件标准,COM不仅仅提供了组件之间的接口标准,它还引入了面向对象的思维。在COM标准中,对象是一个非常活跃的元素,常常被称为COM对象。组件模块为COM对象提供了活动的空间,COM对象以接口的方式提供服务,这种接口就被称为COM接口。COM组件、COM对象、COM接口三者的关系如下图所示:

在Windows系统平台上,一个COM组件可以是一个DLL(Dynamic Linking Library,动态链接库)文件,也可以是一个EXE(可执行程序)文件。一个组件程序可以包含多个COM对象,并且每个COM对象可以实现多个接口。

当另外的组件或者普通程序(即组件的客户程序)调用组件的功能时,它首先创建一个COM对象或者通过其他途径获得COM对象,然后通过该对象所实现的COM接口调用它所提供的服务。当所有的服务结束之后,如果客户程序不再需要该COM对象,那么它就应该释放掉对象所占用的资源,包括对象自身。

1.2 COM接口与API之间的区别

COM接口和经常说的API有点相似。通过API接口层,可以很好地把两个程序连接起来,但存在一些问题:1)、当API非常多时,使用会非常不方便,需要对函数进行组织。2)、API函数需要标准化,按照统一的调用方式进行处理,以适应不同编程语言的实现,包括参数传递顺序、参数类型、函数返回处理都需要标准化。而COM定义了一套完整的接口规范,不仅可以弥补以上API作为组件接口的补足,还充分发挥了组件对象的优势,并实现了组件对象的多态性。

1.3 .NET组件和COM组件的区别

.NET组件和COM组件之间的主要区别在于它们的设计目标、实现方式和运行环境。.NET组件是微软推出的新一代编程模型,用于构建Web应用、桌面应用和移动应用;而COM组件是Windows操作系统中基于二进制代码通信的机制,主要用于实现Windows系统中的各种组件之间的互操作。.NET组件和COM组件在实现方式、编程语言和运行环境上有所不同。

实现方式:

.NET组件:通过C#、VB.NET等.NET编程语言编写,以.NET框架为基础,运行在.NET运行时(CLR)上。

COM组件:通过C++、VB6等编程语言编写,以COM为基础,运行在COM运行时上。

编程语言:

.NET组件:使用C#、VB.NET等.NET编程语言编写,可以跨平台运行。

COM组件:使用C++、VB6等编程语言编写,只能在Windows操作系统中运行。

运行环境:

.NET组件:运行在.NET运行时(CLR)上,支持多语言、跨平台、面向对象和类型安全等功能。

COM组件:运行在COM运行时上,支持多语言、跨平台、面向对象和类型安全等功能。

生命周期:

.NET组件:具有短暂的生命周期,一旦被加载到内存中,就可以立即运行。

COM组件:具有较长的生命周期,需要经过加载、注册、卸载等步骤,需要更多的手动管理。

安全性:

.NET组件:提供了内存管理和类型安全等功能,可以避免缓冲区溢出等安全问题。

COM组件:由于手动管理,容易出现缓冲区溢出等安全问题。

总的来说,.NET组件和COM组件在设计目标、实现方式和运行环境上有所不同,但它们都是用于构建Windows应用程序的组件化编程模型。

因此COM组件并不依赖于.NET Framework的运行环境。

但两者之间是可以相互调用,见如下文章:

COM 互操作示例:.NET 客户端和 COM 服务器 - .NET Framework | Microsoft Learn

1.4 类厂

在创建组件对象时,客户程序调用COM库中的函数进行组件对象的创建工作,COM库的创建函数根据注册表的信息并调用组件程序的入口函数来创建组件对象。所以组件程序需要提供一个标准的入口函数 DllGetObjectClass 函数,用于提供本组件的组件信息。而在 DllGetObjectClass 中,是以类厂的方式获取组件对象的。

类厂,顾名思义,就是COM类的工厂。如果对C++比较熟悉的话,应该会知道设计模式中的工厂设计模式,其实这个类厂的概念就和工厂设计模式很相似。确切的说,类厂应该成为“对象厂”,因为类厂是COM对象的生产基地,COM库通过类厂创建COM对象;COM规定,每一个COM类,对应的都要有一个类厂专门用于该COM类的对象的创建工作。

如果一个组件程序实现了多个COM对象类,则相应的有多个类厂。所以,上述关于字典组件的结构、和多个类厂的结构就如下所示:

1.5 COM库

COM除了定义了组件程序和客户程序交互的规范以外,它也提供了COM的实现部分即COM库,使得这些规范能够真正地应用起来。并且COM库也充当了组件程序和客户程序之间的桥梁,尤其是在组件对象的创建过程中,以及在对象管理、内存管理和一些标准化操作方面起着重要的作用。

COM库的一些常用函数:

客户程序调用COM库创建组件对象的顺序图:

1.6 COM实现过程

COM客户程序、COM库、COM组件程序三者之间的协作过程

1.7 QTActiveX介绍

Qt提供了QtActiveX模块来支持微软ActiveX的开发,Qt的ActiveX和COM的开发支持两种方式:

支持将已有的COM或者ActiveX空间引入到Qt的应用程序中

支持将Qt应用程序或者Qt的对象导出成COM对象或者ActiveX控件供他人使用

具体来说,Qt是通过ActiveXQt框架中的两个模块来支持上述所说的两种方式的:

使用QAxContainer模块,通过QAxObject和QAxWidget分别支持COM对象和ActiveX控件的开发,可以通过这两个对象将外部的COM或者ActiveX组件接入到Qt应用程序

使用QAxServer模块,通过QAxAggregated、QAxBindable和QAxFactory类,通过了进程内和可执行程序exe两种方式的COM Server模式,用来将Qt写的内容导出为COM或者ActiveX供他人使用。

二、基于VS+QT开发Com组件

Qt的windows商业版本提供了ActiveQt这个framework,使用这个组件我们可以在Qt中使用ActiveX控件,并且也开发基于Qt的ActiveX控件。

开源版本是没有的,需要依赖于VS的QT插件来做开发。

2.1 环境配置

2.1.1 VS+QT+vsaddin插件安装

操作参考:

QT - QT中配置MSVC编译环境 以及 VS中配置QT开发环境_qt msvc-CSDN博客

本次最终采用QT6.7 + VS2022版本。

2.1.2 安装相关问题

1.QT安装速度提升,避免各种网络超时报错:

安装QT时,更换镜像源,以带参数的方式启动:

.\qt-unified-windows-x64-4.4.2-online --mirror https://mirrors.tuna.tsinghua.edu.cn/qt

2.VS、QT、MSVC、qtaddin版本对应问题

下载qtaddin插件,与对应VS20xx版本对应即可。

QT5.12.12不支持MSVC2019,最高支持到MSVC2017

QT5.15.2可支持到MSVC2019,但是当前没有离线包的版本,在线安装也不支持;

QT6.x可支持到MSVC2022

MSVC20xx 一般要与对应的VS20xx相对应(参考的两篇文章分别都是对应的)

因此QT所支持的MSVC版本,一般需要跟VS20xx对应起来。

网上成功的:

QT5.15.2 + MSVC 2019 + VS2019

QT5.14.2 + MSVC 2017 + VS2017 

自测:

QT6.7 + MSVC2019 + VS2019  ,新建activex项目,点击生成各种报错,未找到有效解决方案;

QT5.14.2 + MSVC2017 + VS2017,但VS 2017的winform项目,看不到生成的com组件……

QT5.14.2 + MSVC2017 + VS2017(负责生成COM) + VS2022(C#负责调用com),预览界面可以看到组件UI,实际运行显示不了;并且VS2017生成com有不稳定的情况,后续编译不了了……

最终测试版本成功:

QT6.7 + MSVC2019 + VS2022 + QTaddin3.2

注意点:先用IE模式,用html测试生成的Activex控件可用,随后再用winform项目做测试,会好一些。

3. idl报错,生成出现错误“MSB3073”  

  需要使用管理员权限打开VS202软件。

4.ActiveQt/QAxBindable 找不到源文件

安装QT时,ActiveQT组件一定要安装,不然会出现项目找不到active相关头文件的问题:

要修改已有的qt组件,运行QT安装目录下的工具即可:

MaintenanceTool.exe

5."QtWidgets/QWidget”找不到源文件

VS2019 + Qt5.12 配置完成后,无法打开 Qt 源文件解决方案(非常实用)_无法打开qbuttongroup源文件-CSDN博客

2.2 COM(ActiveX)组件开发

QT - QT中的COM编程(dll进程内组件形式)_qt com组件-CSDN博客

2.2.1 实际代码

ActiveQtServer1.h
#pragma once#include <QtWidgets/QWidget>
#include <ActiveQt/QAxBindable>#include "ui_ActiveQtServer1.h"class ActiveQtServer1 : public QWidget, public QAxBindable
{Q_OBJECTpublic:ActiveQtServer1(QWidget *parent = nullptr);public slots://定义两个槽函数,便于外部调用QString getVersion();QString getCurrentTime();private:Ui::ActiveQtServer1Class ui;
};
ActiveQtServer1.cpp
#include "ActiveQtServer1.h"
#include <ActiveQt/QAxFactory>ActiveQtServer1::ActiveQtServer1(QWidget *parent): QWidget(parent)
{ui.setupUi(this);
}
QAXFACTORY_DEFAULT(ActiveQtServer1,"{c5e4017e-73a4-47c2-ad5d-aba20c13a6ba}","{4c7d8024-69c9-4377-8a73-f163a00ad8d8}","{c46481e5-2702-476c-9cb2-e8dca9a23a47}","{484c7403-d8fa-4d7d-ac12-b75f20d6e60b}","{a3dd71cf-f57e-49ef-a6c3-939b4d2e7339}"
)
QString ActiveQtServer1::getVersion() {return "0.0.1";
}
QString ActiveQtServer1::getCurrentTime() {return ui.calendarWidget->selectedDate().toString();
}

2.2.2 生成dll

需要以管理员模式运行VS,才能够正常生成和注册:

2.2.3 发布(需要通过windeploy发布依赖的文件

 D:\Qt\Qt5.12.12\5.12.12\winrt_armv7_msvc2017\bin\windeployqt.exe .\ActiveQtServer1.dll

2.2.3 IE模式 Html测试

从注册表查询classid

编写html文件,替换classid,保存到本地(可以任一目录)

<html>
<head>
<title>activeQtDemo</title>
</head>
<body><object id="233432" width="80%" height="80%"classid="CLSID:869BDCDE-E935-432D-AC52-F66C8F1D27DD"> <PARAM NAME="_Version" VALUE="65536"><!-- 以下为入坑了 --><!--    classid="2F12BFB8-137D-4DC2-9A93-634EFE5A6DFC">  1D991CF8-6F9D-4574-9507-B526D699F4321D991CF8-6F9D-4574-9507-B526D699F432-->  [Object not available! Did you forget to build and register the server?]</object>
</body>
</html>

edge浏览器配置白名单,支持IE模式(需要支持Activex的浏览器)

通过IE浏览器打开:

点击允许加载插件

2.2.4 更新Com组件:重新生成*.dll无法打开问题

查看占用进程:

dllhost.exe对应进程kill

devenv.exe 对应VS winform调用方关掉工程即可

三、QT QtWidgetApp调用COM

3.1 操作参考文章        

Qt调用Com组件--QT调用COM组件DLL(dumpCPP工具)_qt dumpcpp dll-CSDN博客

3.2 实际代码

main.cpp

#include <QApplication>
#include <QAxObject>
#include <QDebug>
#include <QFile>int main(int argc, char *argv[])
{QApplication a(argc, argv);QAxObject *mpAxObj;mpAxObj = new QAxObject();//指定调用的COM组件类ID(clsid\ClassID),这个ID要填正确,就是前面宏定义的 ClassID.mpAxObj->setControl("{c5e4017e-73a4-47c2-ad5d-aba20c13a6ba}");//导出支持调用的函数接口QString DOC = mpAxObj->generateDocumentation();QFile outFile("com_function.html");outFile.open(QIODevice ::ReadWrite|QIODevice ::Text);QTextStream TS(&outFile);TS<<DOC<<endl;//调用COM组件函数接口: 显示界面mpAxObj->dynamicCall("show()");//调用COM组件函数接口:获取版本QString result=mpAxObj->dynamicCall("getVersion()").toString();qDebug()<<"插件的版本号:"<<result;//调用COM组件函数接口:获取当前时间QString result2=mpAxObj->dynamicCall("getCurrentTime()").toString();qDebug()<<"当前时间:"<<result2;return a.exec();
}

四、VS winform调用COM

4.1 winform工程引用com组件

操作参考:

C#-winform调用COM组件(COM组件由Qt开发)-云社区-华为云

Qt开发Activex笔记(三):C#调用Qt开发的Activex控件_qt开发ocx给c#-CSDN博客

4.2 修改生成的目标平台为x64

4.3 运行最终效果

4.4 遇到的问题:

4.4.1 点击运行后,报错没有注册类

System.Runtime.InteropServices.COMException

  HResult=0x80040154

  Message=没有注册类 (异常来自 HRESULT:0x80040154 (REGDB_E_CLASSNOTREG))

  Source=System.Windows.Forms

  StackTrace:

   at System.Windows.Forms.UnsafeNativeMethods.CoCreateInstance(Guid& clsid, Object punkOuter, Int32 context, Guid& iid)

   at System.Windows.Forms.AxHost.CreateWithLicense(String license, Guid clsid)

   at System.Windows.Forms.AxHost.CreateInstanceCore(Guid clsid)

   at System.Windows.Forms.AxHost.CreateInstance()

   at System.Windows.Forms.AxHost.GetOcxCreate()

   at System.Windows.Forms.AxHost.TransitionUpTo(Int32 state)

   at System.Windows.Forms.AxHost.CreateHandle()

   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)

   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)

   at System.Windows.Forms.AxHost.EndInit()

   at WindowsFormsApp4.Form1.InitializeComponent() in D:\VisionProject\VSWorkSpace\WinformWS\WindowsFormsApp4\WindowsFormsApp4\Form1.Designer.cs:line 64

   at WindowsFormsApp4.Form1..ctor() in D:\VisionProject\VSWorkSpace\WinformWS\WindowsFormsApp4\WindowsFormsApp4\Form1.cs:line 17

   at WindowsFormsApp4.Program.Main() in D:\VisionProject\VSWorkSpace\WinformWS\WindowsFormsApp4\WindowsFormsApp4\Program.cs:line 19

项目属性,生成的目标平台修改为x64。

4.4.2 控件已经成功添加到工具箱中,但未在活动设计器中启用

问题描述:Visual studio 2022 添加com组件到工具箱错误提示:

下列控件已经成功添加到工具箱中,但未在活动设计器中启用 ,请确认要添加的控件能够兼容当前设计器和.net framework 版本。

修改方法:

要选择上面这个Windows窗体应用(.NET Framework)

【Windows 窗体应用】的窗体属性中还有其他信息,目标框架:.NET Core 3.1

而【Windows 窗体应用(.NET Framework)】,其框架则是.NET Framework

这个.NET Core与 .NET Framework是完全不一样的东西:

  • .NET framework框架开发出来的应用只能在windows上运行。
  • .netcore 是开源的,开发出来的应用可以跨平台运行,比如运行在MAC,Linux上 。

而我们添加的COM组件,实际上是只应用于windows环境的技术,在一个非windows 的底层技术以及上层环境肯定就是不行的了。

4.4.3 引入控件报错

1.在工具箱中,拖入控件到UI中,会弹窗报错:

直接重新生成项目,也会报错:

生成的dll确实是64位的:

2.修改为x64平台(上述第二章的qt com dll也是基于x64编译的)后,编译正常,且AxActiveQTServer2Lib不再报错

五、最终代码demo

https://download.csdn.net/download/u011490813/89246081icon-default.png?t=N7T8https://download.csdn.net/download/u011490813/89246081

参考资料

COM简介

COM - COM的简单介绍_com组件结构-CSDN博客

windeployqt打包Qt应用程序(Com只注册了,还不够,需要通过windeploy发布依赖的文件):

windeployqt打包Qt应用程序_qt windeployqt 打包-CSDN博客

Qt的进程间通信,以Active服务器的形式,手把手教你VS上进行Qt的COM、ActivedQt Server的开发,比保姆还保姆

https://www.cnblogs.com/Leventure/p/16971934.html

VS+QT插件创建qt 的ActiveQT Server工程踩过的坑_qt activeqt server-CSDN博客


http://www.mrgr.cn/p/08610655

相关文章

BIO、NIO与AIO

文章目录 一 BIO同步阻塞案例BIO模式消息多发多收实现 二 NIONIO核心组件Buffer(缓冲区)Buffer常见方法缓冲区的数据操作直接内存与非直接内存 Channel(通道)channel常用操作 Selector(选择器)selector选择器处理流程NIO非阻塞式网络通信原理分析 NIO网络编程实现群聊系统服务端…

第6讲需求分析--uml用例图

用例图 一.识别参与者 参与者在系统外所以画图时画在框外。系统用例图比业务用例图更完善所以经常让画的是系统用例图。在商品销售系统里:参与者是顾客。 在会计系统里:参与者是商品销售系统。 二.关系 1.参与者之间 泛化关系子指向父 2.参与者和用例之间 关联关系3.用例和用…

AI大模型探索之路-训练篇10:大语言模型Transformer库-Tokenizer组件实践

系列篇章&#x1f4a5; AI大模型探索之路-训练篇1&#xff1a;大语言模型微调基础认知 AI大模型探索之路-训练篇2&#xff1a;大语言模型预训练基础认知 AI大模型探索之路-训练篇3&#xff1a;大语言模型全景解读 AI大模型探索之路-训练篇4&#xff1a;大语言模型训练数据集概…

认知提升的方法

认知提升的方法一、什么是认知 经验是对于过往经历的总结归纳,当把这种经验传授给别人时,这种经验对别人来说就是知识。所以,知识是人脑对客观事物的信息沉淀。 技能是人们通过练习而获得的动作方式和系统,例如操作技能中的PS技术、木工技术、电工技术、水工技术等,而能力…

万兴PDF专家 PDFelement Pro v10.3.8 破姐版!

&#x1f9d1;‍&#x1f4bb;万兴PDF专家 PDFelement Pro v10.3.8 破姐版 (https://docs.qq.com/sheet/DRVVxTHJ3RXJFVHVr)

QBXT五一集训DAY1笔记

\(Day 1\) \(ASCII\) 简单来说,\(ASCII\) 其实就是字符与数字之间的映射 比如说,\(a\) 的 \(ASCII\) 就是 \(97\) 模运算:% 来复习一下小学数学:\(a/b=c……d\) 这里的\(d\) 就是 \(a\) 除以 \(b\) 的余数,在计算机中,用%来表示 通过这个式子,我们进而得出 \(a=b*c+d\) …

POETIZE个人博客系统源码 | 最美博客

源码介绍 POETIZE个人博客系统源码 | 最美博客 这是一个 SpringBoot Vue2 Vue3 的产物&#xff0c;支持移动端自适应&#xff0c;配有完备的前台和后台管理功能。 网站分两个模块&#xff1a; 博客系统&#xff1a;具有文章&#xff0c;表白墙&#xff0c;图片墙&#xf…

c#胖东来小程序自动购物程序(接单,windows桌面程序、linux程序、网络应用等等)

一、程序效果 自动打开胖东来小程序,自动购物 二、实现 先截屏,然后利用opencv库识别下一步按键所在位置,然后使用mouse_event控制鼠标,模拟人的动作 第一步,截取屏幕static Bitmap CaptureScreen() {int screenWidth = Screen.PrimaryScreen.Bounds.Width;int screenHeig…

黑马点评项目个人笔记+项目优化调整

博客须知 本篇博客内容来源与黑马点评项目实战篇-16.用户签到-实现签到功能_哔哩哔哩_bilibili&#xff0c;作者对视频内容进行了整合&#xff0c;由于记笔记时图片使用的是本地路径&#xff0c;所以导致博客的图片无法正常显示&#xff0c;如果有图片需求可以下载上方的pdf须…

React复习笔记

基础语法 创建项目 借助脚手架&#xff0c;新建一个React项目(可以使用vite或者cra&#xff0c;这里使用cra) npx create-react-app 项目名 create-react-app是React脚手架的名称 启动项目 npm start 或者 yarn start src是源文件index.js相当于Vue的main.js文件。整个…

优先级翻转实验以及分析现象

优先级翻转实验以及分析现象 优先级翻转指的是高优先级的任务反而慢执行,低优先级的任务反而优先执行。优先级翻转在抢占式内核中是非常常见的,但是在实时操作系统中是不允许出现优先级翻转的,因为优先级翻转会破坏任务的预期顺序,可能会导致未知的严重后果。上图中三个任务…

十日冲刺其六

今天完成了:用户可以自主删除自己发表的空间动态 遇到的困难:不能在浏览空间时进行删除动态,限制因素较多 明天打算,优化一下动态删除功能

Swift - 可选项(Optional)

文章目录 Swift - 可选项&#xff08;Optional&#xff09;1. 可选项&#xff08;Optional&#xff09;2. 强制解包&#xff08;Forced Unwrapping&#xff09;3. 判断可选项是否包含值4. 可选项绑定&#xff08;Optional Binding&#xff09;5. 等价写法6. while循环中使用可选…

Day27:阻塞队列、Kafka入门、发送系统通知、显示系统

阻塞队列BlockingQueue BlockingQueue 解决线程通信的问题。阻塞方法:put、take。 生产者消费者模式 生产者:产生数据的线程。消费者:使用数据的线程。 &#xff08;Thread1生产者&#xff0c;Thread2消费者&#xff09; 实现类 ArrayBlockingQueueLinkedBlockingQueuePr…

React + 项目(从基础到实战) -- 第十期

目标 学会react 状态管理工具 使用redux管理用户状态 Context 跨层级传递,不像props层层传递类似于Vue的provide/inject用于:切换主题颜色,切换语言 useReducer useState 的替代方案 简化版的redux MobX 1. MobX 介绍 MobX 中文文档 声明式的修改数据 , 像vue state ac…

将社会脆弱性纳入高分辨率全球洪水风险绘图

将社会脆弱性纳入高分辨率全球洪水风险绘图 贡献 将高分辨率流洪水模型的年平均超标概率估计值与网格化人口和贫困数据相结合,创建了 90 米分辨率的全球洪水脆弱性调整风险指数(VARI Flood)。该指数提供了国家内部或国家之间相对风险的估计值,并通过识别以高密度和高社会脆…

模型智能体开发之metagpt-多智能体实践

参考&#xff1a; metagpt环境配置参考模型智能体开发之metagpt-单智能体实践 需求分析 之前有过单智能体的测试case&#xff0c;但是现实生活场景是很复杂的&#xff0c;所以单智能体远远不能满足我们的诉求&#xff0c;所以仍然还需要了解多智能体的实现。通过多个role对动…

基于springboot+vue+Mysql的漫画网站

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

带宽的理解-笔记

带宽的理解 带宽(频带宽度)&#xff1a;是指电磁波最高频率和最低频率的差值&#xff0c;这一段频率被称为带宽。 举例说明 人耳能听到的频率范围是20赫兹到2万赫兹。换句话说&#xff0c;人而只对20赫兹至2万赫兹的声音频率有反应&#xff0c;超出或低于这一频率范围的声音我…

【C++】哈希的应用---位图

目录 1、引入 2、位图的概念 3、位图的实现 ①框架的搭建 ②设置存在 ③设置不存在 ④检查存在 ​4、位图计算出现的次数 5、完整代码 1、引入 我们可以看一道面试题 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数…