Qt大型工程开发技术选型Part3:Qt调用C#编写的COM组件实例以及错误总结

ok,前面铺垫了那么多,现在来写一个开发实例,我会把其中隐藏的坑和陷阱简单谈谈,并在文章最后总结。

不愿意看长篇大论的可以直接看实例:CS_COM_Build

废话不多说直接起步。

先说场景,我这边是一个C#的DLL,然后让一个COM组件去加载这个DLL,然后再让Qt去调用这个C#的COM组件,也就是说有三个工程,如图所示:

其中FrameWork是一个窗体的DLL,如下图左边所示,右边是QtController的窗体,MiddleCOM则是充当了一个中间件,用于为普通的C#DLL提供COM服务。

如图所示,两个窗体之间可以进行交互,其中Qt应用程序是主程序,而C#的窗体程序则是以COM组件形式发布的。

一、写一个窗体

ok话不多说,先来写一个窗体:

using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms; namespace FrameWork
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
} private void Form1_Load(object sender, EventArgs e)
{ }
//声明一个委托,可以向外部发送消息
public delegate void SendMessageOutEventHandler(System.String strValue);
public event SendMessageOutEventHandler SendMessageOut; private void button1_Click(object sender, EventArgs e)
{
this.SendMessageOut(this.textBox1.Text);
}
//接收消息,展示到窗体上
public void getMessage(System.String strValue)
{
this.richTextBox1.AppendText(strValue);
}
}
}

二、写一个COM的中间件

现在我们来做套壳的COM组件。先添加一个C#的类库(这不会也要教吧)



AssemblyInfo.cs中,讲这个[assembly:ComVisuble(false)] 改为true

右键这个COM工程,点击属性,找到为COM互操作注册

如果没有这一步,可能会导致Qt在调用的时候弹出报错提示CoCreateInstance failure(系统在找不到指定文件。),原因是如果没有互操作注册,在注册表中你去找到你的这个类,你会发现少了几行,比如BaseCode,就会导致COM在注册的时候找不到实际的代码文件,无法找到DLL文件进行加载。

然后我们来写一下这个COM导出类,注意事项都在代码内,可以自己看看

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks; namespace MiddleCOM
{ //Author:Leventure
//DateTime:2022.12.22
//Description:一个C# COM组件实例 //注:C#中的类,无论是方法还是事件,都需要通过接口的形式向外公布,方法接口需要通过方法接口去提供服务,如果只是类内提供的函数或者事件,在外部可能无法正常使用。
//注2:导出接口可以不需要设置ComVisible(true),这样可以使得导出的接口更加清晰
//注3:这个Guid是唯一的,可以使用VS提供的Guid生成工具生成 //方法接口,向外提供方法,注意[InterfaceType(ComInterfaceType.InterfaceIsDual)]的声明这样的接口是双向的
[Guid("7EEDF2D8-836C-4294-90A0-7A144ADC93F9")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IOutClass
{
[DispId(1)]
void getMessage(System.String strValue);
} //事件接口,注意[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]这代表这个是用作事件处理
[Guid("7FE32A1D-F239-45ad-8188-89738C6EDB6F")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IOutClass_Event
{
[DispId(11)]
void SendMessageOut(System.String strValue);
} [Guid("76BBA445-7554-4308-8487-322BAE955527")]
[ClassInterface(ClassInterfaceType.None)] // 指示不为类生成类接口。如果未显式实现任何接口,则该类将只能通过 IDispatch 接口提供后期绑定访问。这是 System.Runtime.InteropServices.ClassInterfaceAttribute
// 的推荐设置。要通过由类显式实现的接口来公开功能,唯一的方法是使用 ClassInterfaceType.None。
[ComDefaultInterface(typeof(IOutClass))] // 以指定的 System.Type 对象作为向 COM 公开的默认接口初始化 System.Runtime.InteropServices.ComDefaultInterfaceAttribute
// 类的新实例。
[ComSourceInterfaces(typeof(IOutClass_Event))] //使用要用作源接口的类型初始化 System.Runtime.InteropServices.ComSourceInterfacesAttribute 类的新实例。
[ComVisible(true)] //提供COM的可访问性
[ProgId("IOutClass")] //给这个导出类一个名称
public class OutClass : IOutClass
{
private FrameWork.Form1 form1 = null;
public OutClass()
{
if(form1 == null)
{
this.form1 = new FrameWork.Form1();
this.form1.SendMessageOut += new FrameWork.Form1.SendMessageOutEventHandler(this.SendMessageReceived);
this.form1.Show();
}
}
//提供方法向C#DLL发送消息
public void getMessage(System.String strValue)
{
if(this.form1 != null)
{
this.form1.getMessage(strValue);
}
}
//这个是从方法类中继承来的向外发送消息事件
public delegate void SendMessageEventHandler(System.String strValue);
public event SendMessageEventHandler SendMessageOut;
//从C#DLL中传来的消息,转发给COM服务器
private void SendMessageReceived(System.String strValue)
{
//向外发送消息
SendMessageOut(strValue);
}
}
}

ok,到这里我们的COM组件之旅几乎就已经完成了。编译这个DLL之后我们需要将其注册到我们的系统中去。这里.net的DLL和非托管的DLL的注册方式不一样。非托管的DLL注册可能是通过直接在cmd中输入regsvr32 xxx.dll进行注册,但是.net的DLL需要通过它.net自己的工具进行的注册,我们可以在菜单栏中找到vs的开发者工具,如图所示

(我这里是用的VS2019开发,其实这里用的只是regasm.exe这个工具,用哪个版本的无所谓)

调用命令如下:

注:如果你用的是系统提供的注册工具regsvr32注册的话,会提示你没有DllServerRegister入口点,请检查是否是dll或者ocx,这个是因为.net框架提供的COM组件是没有给定这两个东西的,所以需要用他们自己的工具!

OK,这个时候应该就已经注册完成了,为了检验成果我们可以去Windows系统注册表中查看,比如我们这里声明的导出类的名称为[ProgId("IOutClass")],就在注册表内查找 IOutClass,或者查找导出类对应的Guid,应该就能查找到对应的内容

比如这个路径下

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Classes\CLSID{76BBA445-7554-4308-8487-322BAE955527}\ProgId



如果出现了这一条,就说明你的注册成功了

三、编写Qt程序来调用COM组件

这部分内容比较简单,主要是提一下报错:

直接上界面和代码吧,这部分没有什么需要特别注意的:

#pragma once

#include <QtWidgets/QMainWindow>
#include "ui_QtController.h"
#include "qaxobject.h"
#include "qfile.h"
#include "qtextstream.h"
#include "qdebug.h"
class QtController : public QMainWindow
{
Q_OBJECT public:
//构造函数里调用了这个Init,懒得写了,将就着看吧
QtController(QWidget *parent = nullptr);
~QtController();
QAxObject ax_test;
void Init() {
this->ax_test.setControl("IOutClass");
//获取接口文档
QString interfaces = ax_test.generateDocumentation();
QFile docs("AX_Interfaces.html");
docs.open(QIODevice::ReadWrite | QIODevice::Text);
QTextStream TS(&docs);
TS << interfaces << endl; qDebug() << QObject::connect(&this->ax_test, SIGNAL(SendMessageOut(QString)), this, SLOT(getMessageFromCS(QString)));
} private slots:
void on_pushButton_clicked() {
this->ax_test.dynamicCall("getMessage(QString)", this->ui.lineEdit->text());
}
void getMessageFromCS(QString strValue) { this->ui.textEdit->append(strValue); }
private:
Ui::QtControllerClass ui;
};

这样基本上就能保证调用了,这里提几个BUG,也是我们困扰了很久的地方:(凭借记忆写的,不一定全对哈)

四、查漏补缺、总结:

1.调用时提示 No Such Property、或者如图所示:

答:就我们目前的经验来看,有可能是你的COM组件中提供的属性有问题,你依赖了其他的DLL,但是这个被依赖的DLL它的依赖可能没被加载,也就是说缺少了部分依赖,需要你自己补全所有依赖。

2.调用时报错UnKnownError,如图:

答:这有可能是因为被调用的COM组件的位数大于调用方的位数导致的,比如32位的应用程序调用64位的进程,可能会导致UnKnown Error。

3.报错提示CoCreateInstance failure(系统找不到指定的文件。)

答:有可能是由于你在编写COM组件的时候没有在COM组件的属性中勾选上COM互操作选项,导致注册表内BaseCode行未注册成功。

4.注册DLL时,显示找不到入口点DllRegisterServer

答:需要用.net提供的COM注册服务软件regasm,具体使用方法见上方。

5.注册的C# COM 组件,为什么Event变成了 add_xxx(IDispatch *value) 和remove_xxx(IDispatch *value) 了?(注:xxx是事件的名称)

答:事件需要通过继承事件接口来继承暴露,函数需要通过函数接口来继承暴露,这样会变成另外一种形式,我没用过,我不能确保能不能用。

还有什么问题可以提问,我看到了就会回答,如果需要私聊可以联系我的Github提交issue

Qt大型工程开发技术选型Part3:Qt调用C#编写的COM组件实例的更多相关文章

  1. 移动应用开发技术选型:WebApp>HybridApp>NativeApp

    一:概念辨析 Web App:生存在浏览器里的应用,只能运行在浏览器里,宿主是浏览器,不是操作系统.资源一般都在网络上,就是一个触屏版的网站.如:微信公众号.不需要在设备上下载安装,只需通过浏览器即可 ...

  2. Web开发技术选型之Java与PHP

    PHP与J2EE的对比 网上有很多关于PHP与J2EE之间的对比,细观无非以下几点: 1.语言特征 PHP为脚本语言,解释型语言,弱类型,专为Web开发打造.Java为C语言系编程语言,编译型,强类型 ...

  3. 关于APP,原生和H5开发技术的争论 APP开发技术选型判断依据

    关于APP,原生和H5开发技术的争论 App的开发技术,目前流行的两种方式,原生和Html5.原生分了安卓平台和ios平台(还有小众的黑莓.死去的塞班就不说了),H5就是Html5. 目前争论不休的问 ...

  4. Android 开发技术选型(博客,新闻,阅读类)

    前言 最开始学习写应用的时候,发现类聚合数据这个平台可以提供一些免费数据接口,于是写了个人的第一个应用-– JuheNews,当时的知识储备稍显粗糙,虽然现在的知识也不咋滴,但是相对之前而言还是有些进 ...

  5. Qt调用Delphi编写的COM组件

    这个问题捣鼓了两天,现在终于解决了,做个笔记分享给大家,以免走弯路 起初,我的想法是在DLL中写一个interface并从函数中导出这个interface,像这样的代码 ICom1 = interfa ...

  6. 国内混合APP开发技术选型

    http://www.sunzhongwei.com/weex-react-native-ionic-technology-selection 选谁? 企业级应用是要考虑性能和流畅度的, 如果只是做个 ...

  7. JavaScript学习笔记之JavaScript调用C#编写的COM组件

    1.新建一个C#类库项目:MyCom: 2.修改 Properties 目录下的 AssemblyInfo.cs(程序集文件) 中的 ComVisible 属性为 true: 3.项目->属性- ...

  8. [JNA系列]Java调用Delphi编写的Dll之实例Delphi使用PWideChar

    Delphi代码 unit UnitDll; interface uses StrUtils, SysUtils, Dialogs; function DoBusinessWide(pvData: P ...

  9. [JNA系列]Java调用Delphi编写的Dll之实例Delphi使用PAnsiChar

    Delphi代码 unit UnitDll; interface uses StrUtils, SysUtils, Dialogs; function DoBusinessAnsi(pvData: P ...

  10. 快速全面了解QT软件界面开发技术

    快速全面了解QT软件界面开发技术     目录 前言 一. 学习QT可能的目的是什么? 只想体验一下QT? 当前的项目选择了用QT. 为将来做QT技术储备. 二. QT的核心技术优势是什么? QT在软 ...

随机推荐

  1. 《吐血整理》高级系列教程-吃透Fiddler抓包教程(25)-Fiddler如何优雅地在正式和测试环境之间来回切换-下篇

    1.简介 在开发或者测试的过程中,由于项目环境比较多,往往需要来来回回地反复切换,那么如何优雅地切换呢?宏哥今天介绍几种方法供小伙伴或者童鞋们进行参考. 2.实际工作场景 2.1问题场景 (1)已发布 ...

  2. ERP 系统最重要的是什么?

    ERP系统最重要的就是内部业务逻辑,这也是ERP复杂.专业性的体现!ERP系统可以算是当今最复杂的应用系统,也是最昂贵的,头部厂商一套系统动辄上千万,实施费用也常常达到千万级,实施周期动辄半年一年的, ...

  3. Node.js(二)express

    npm init -y(初始化项目) npm install express(引入express) npx express-generator -e(自动生成模板.添加对 ejs 模板引擎的支持) a ...

  4. gitee仓库上传文件的步骤

    一:Git是什么? Git是一种代码托管技术.在开发中,Git是一种代码托管技术,很多代码托管平台也是基于Git来实现的.Git可以帮我们做到很多的 事情,比如代码的版本控制,分支管理等. 可以把Gi ...

  5. git(新)

    Git仓库的工作分区 工作区到暂存区的操作 git init :在当前文件夹创建一个文档库,自动产生一个master分支.当当前文件夹已有文档库时,不会再次创建也不会修改,只会将隐藏的.git文件夹显 ...

  6. Windows应急响应——敬请期待!

    检查内容 进程.服务.用户.网络连接.漏洞补丁.木马查杀. 工具 火绒剑. 防护措施 杀毒软件

  7. 一天五道Java面试题----第九天(简述MySQL中索引类型对数据库的性能的影响--------->缓存雪崩、缓存穿透、缓存击穿)

    这里是参考B站上的大佬做的面试题笔记.大家也可以去看视频讲解!!! 文章目录 1.简述MySQL中索引类型对数据库的性能的影响 2.RDB和AOF机制 3.Redis的过期键的删除策略 4.Redis ...

  8. 自建流媒体如何录制视频。齐博x1齐博x2齐博x3齐博x4齐博x5齐博x6齐博x7齐博x8齐博x9齐博x10

    http://x1.eapis.site/ 先打开配置文件\conf\config.php 里边的内容大概如下,第一项是必须要配置的,换成你的网站域名网址.第二项,如果流媒体服务器配置了https证书 ...

  9. 乾象投资:基于JuiceFS 构建云上量化投研平台

    背景 乾象投资 Metabit Trading 成立于2018年,是一家以人工智能为核心的科技型量化投资公司.核心成员毕业于 Stanford.CMU.清北等高校.目前,管理规模已突破 30 亿元人民 ...

  10. C#接口设计

    基本架构 model 实体 说得通俗一些,model中主要是定义前端传过来的变量以及其类型,或者是需要操作的数据库中的某些字段值.举个例子: namespace Test.Models.Book { ...