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

写在前面,文中的ID有部分对不上,因为我中途改了一下,我建议你在实际的开发中自己去跟注册表中暴露出来的对一下.

一、Qt开发COM组件背景:COM组件是什么?为什么要用COM组件?

我是个Windows平台上的程序员,开发主要以Windows应用程序为主。目前在当前平台上我们做了很多进程间通信开发的尝试,包括但不限于Windows窗体消息(SendMessage等),共享内存,COM组件,已知的进程间通信方案还有socket通信(没必要,一般用于网络通信)和D-Bus方案(部分平台貌似不兼容,尚未了解)。

关于上述方案,Windows窗体消息和共享内存的方案我居然没写博客,之后可能会补上,这里暂时不做讨论。

先说开发场景:现在我有一个主框架或者说主程序,会有很多别的模块需要在外部开发,这些模块可能会需要用到主框架中的信息,或者说一些接口来调用一些特定的功能。比如说我一个课堂教学模块,可能需要实现屏幕广播,语音广播,资料下发等功能。出于低耦合的基本原则,我们当然不希望每个这样的教学模块里都实现一个类似的功能,这样不仅是对开发资源的极大浪费,也会极影响整个程序的维护难度。所以我们希望所有类似的功能都集成到一个主框架里,任外部的模块去调用功能,将二者隔离开来,这样不仅极大地提高了程序复用的可能,也能提高开发速度。

这个和之前的SendMessage方式最大的区别就是:如果想用SendMessage传递信号,首先需要两个应用程序之间互相获得句柄就挺麻烦的,需要从启动项传入然后再给予回执。其次SendMessage可能导致线程间不同步等情况出现,可能会导致部分程序的渲染、启动等县城不同步产生的意外问题。最主要的一点,SendMessage智能发送消息码,而这些消息码都是需要提前指定好的,而且接收方也不一定能统一,所以需要特定的消息码特定处理,很难进行全局的消息码处理。这些都是在之前的主框架开发中暴露出来的问题。

但是窗体消息有没有什么优点呢?当然是有的,最大的优点就是可靠,且开发简单。消息的发送是相当可靠的,而且一般的框架都会有一个关于窗体消息的收发机制,只需要重写或者指定接收函数即可,对于小组件或者进程内部可以采取类似的方法。

但我们是一个中心组件,是为了给其他进程提供服务的,所以我们这个中心服务需要更加简单,外部更加易用,所以我们要向所有模块的开发者提供适当的接口以调用,而且接口必须足够简单。

COM组件只需要暴露一些需要暴露在外部的类即可,比如我需要向外部暴露一个屏幕广播的接口,那就可以让其他的用户去通过COM组件去调用这个接口,至于具体是怎么实现的,这不是开发人员需要关心的内容,只需要模块的开发人员简单的调用接口,就像魔法一样,让功能在外部实现即可。

而主框架的开发人员也不需要把每个工具分门别类地用不同的进程管理工具去一个个进程单独分析,管理他们这个那个的消息码,或者管理这个那个具体行为,而是更多地把精力放在具体功能的具体接口上,也就不存在上下级模块调用的问题了。

现在让我们来说一下关于Qt如何进行COM组件的开发,或者说ActiveX控件的开发。ActiveX是Microsoft对于一系列策略性面向对象程序技术和工具的称呼,其中主要的技术是组件对象模型(COM)。在有目录和其它支持的网络中,COM变成了分布式COM(DCOM)。

国内外论坛关于Qt开发COM组件和ActiveQt Server开发的资料和讨论并不多,官方文档里也并没有提供我需要知道的资料,所以我这里写的东西不一定完全正确,如果我写的内容有问题,欢迎给我发送Issue提供一些相关的咨询或者疑问,也欢迎讨论。

二、我们做什么?

由上所述,我们想做一个可执行程序EXE,希望在这个程序运行的时候,暴露出一些接口供外部进程调用,来调用这个进程里面的一些类和方法,在这个进程正在执行的时候,可以通过COM组件的消息来执行指定的任务。

但是我们在实际开发过程中,发现ActiveQt Server编写的EXE并不能向调用发发送Signal消息。也就是说,在我们开发完COM组件后,启动该COM组件EXE,此EXE能够接收到调用方的请求,而且如果发送信号的事件是在请求的函数中执行的,那么该信号也可以成功发送给调用方。但是如果我是在COM组件EXE内主动发送的这个信号,比如点击按钮,那么接收方就接收不到这个信号了,不能达到双向通信的目的。目前尚未查明原因,所以我们只能退而求其次。

也就是我们通过一个COM组件来做中间件,让一个COM组件做单纯的中间件,只负责发送信号和接收事件,主框架和其他的工具都通过这个中间件来执行命令和接收消息。

三 我们怎么做?

根据上面的需求,我们就有了三个程序:主程序 COM中间件 子程序 ,主程序和子程序我在博客里就不提供了,这里来说一下COM中间件如何开发

这里选Qt ActiveQt Server选项,它就会给你把默认全部设置好了

可以看到,这里多了一串

QAXFACTORY_DEFAULT(ActiveQtServer1, //暴露给外部的类的名称,我们这里暴露的类名称为ActiveQtServer1,先就这么写
"{82a36901-0766-498b-beaf-8b3e62e0b530}", //类ID,如果是一个DLL需要暴露多个类的话,调用的时候是通过这个ID连接到指定的类
"{b8de8377-4185-4c9d-a803-77b1939b1360}", //接口ID,不用管
"{70744dbb-3062-4ade-9a0c-fc42dafa5b8f}", //事件ID,不用管,如果当前的类需要发送事件的话需要定义
"{f763f5b7-cc63-4a05-9757-9debc4a7078d}", //当前lib 的ID,这个是这个lib的唯一标识,如果只有一个类导出,我们可能会需要用到这个东西
"{cd0da224-8eec-4739-a342-ecf88f6d3259}" //当前进程ID,这个如果是你将这个COM组件设定为EXE,那么我们可能就需要通过这个来建立连接

这个宏,这个宏命令的作用是为了给定一个GUID,设定一些默认的值,适用于一个DLL只需要暴露一个类的情况,如果一个DLL需要暴露多个类,则需要另外打算。

我们换一种写法,这样可以让这个结构更清晰:

我们在想要暴露的类中定义宏如下:

在这个类里定义了有关这个类的属性,也就是ClassID,InterfaceID,EventsID,然后我们还需要在总的宏编译(如果是EXE则可以直接加到main.cpp中,如果是DLL的话随便找一个头文件,反正是最后编译的位置处随便写就行了)处定义我们需要导出的类,内容如下:

如果是dll,我们直接把这一段放在cpp文件的最后就可以了

类似如此,注意这个#include "qaxfactory.h"要写在QAXFACTORY_BEGIN()的前面,因为这个宏本身也是qaxfactory.h带来的。这里QAXCLASS()中带着的是我们希望在这个QAxtive server中带出的类,如果我们有很多的类,可以以类似的方式来导出,就不用它默认的宏了。

另外需要注意的一点是,因为我们这个类默认带了个QWidget的类,所以会同时导出很多父类的乱七八糟的槽函数和信号会跟随其一同导出,目前我还没有想到该怎么屏蔽掉这些东西不让其导出,之后也许会找到,我会放在这里。

总而言之我们现在试着编译一下

当出现这个指令的时候,说明我们的dll编译成功了,在编译的时候我们还同时注册了这个dll。如果我们的程序要挪到客机上去使用,就需要在控制台中输入regsvr32 xxx(dll名称).dll来注册这个dll控件。

当然了,除了注册,还需要给这个dll加载一些依赖,具体怎么做详情参考windeployqt 打包Qt应用程序 打包完毕后,这个COM组件就算是可以发布了

整体流程说完了,那我们现在该往里面写点东西了

写一个信号,一个函数,一个变量来举例。注意,函数和变量都必须是公用的才是可用的,否则是调用方不可见的

其中红色的代表变量控制,黄色的代表接口,蓝色代表了信号事件。注意变量控制的写法,因为不管是读取还是写入变量其本质还是通过接口去实现的,这个Q_PROPERTY的宏是为了让调用方更好地操作ActiveX控件。

然后我们可以去注册表里面搜我们这个LIB的ID,找一下我们这个COM组件的名称:

搜索regedit打开注册表,搜索我们Class的ID : A9787707-850D-4D42-BB09-5549713B008F,找到我们这个输出的类的唯一标识:

计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\ActiveQtServer1.ActiveQtServer1\CurVer

由上可见我们这个类的名称是ActiveQtServer1.ActiveQtServer1.1 这个是我们COM组件名称的唯一标识,这个要记住,后面要用

那么这边Qt的COM组件开发就结束了,现在我们来看一下怎么调用这个COM组件

3.如何调用COM组件?

我们新建一个项目 Qt_ComTest_Client如图

我们调用COM组件主要通过QAxObject 或者QAxWidget实现,这里我就用QAxWidget 大体来说没很大区别,习惯上可以用QAxWidget,比较方便

  QAxWidget *ax_test;
ax_test = new QAxWidget();
ax_test->setControl("ActiveQtServer1.ActiveQtServer1.1"); //这里通过名字就可以直接找到COM组件了,如果你想通过COM组件ID也是可以的
//ax_test.setControl("17C4C136-0EC8-4EF9-B2DF-891A4DBA6E6D"); QString interfaces = ax_test->generateDocumentation(); //解析COM组件的接口文档 QFile docs("AX_Interfaces.html");
docs.open(QIODevice::ReadWrite | QIODevice::Text);
QTextStream TS(&docs);
TS << interfaces << endl;//将COM组件的文档尝试写入到一个html页面里面去,可以在里面看到有什么内容 this->ui.plainTextEdit->appendPlainText(interfaces);

在这里我们就可以看到COM 组件提供的内容了,我们在程序根文件夹里面找到这个html文件,看一下内容

可以找到我们定义的事件 add 和 test,点击一下 还有这个东西怎么用的提示

然后我们可以尝试绑定信号,并尝试引用内容。调用方法用dynamicCall("function_name(params)",params),接收信号通过QObject::connect(object,SIGNAL,eceiver,SLOT())的形式,调用参数通过property("object_name")的形式

来试一下

尝试触发事件,按下按钮:

void Qt_ComTest_Client::on_pushButton_clicked()
{
qDebug() << "cliecked";
QVariantList params = { 100,200 };
qint32 result = ax_test->dynamicCall("add(int,int)", params).toInt();
this->ui.plainTextEdit->appendPlainText(QString::number(result));
}

来尝试接收一下信号

connect(ax_test, SIGNAL(test(int)), this, SLOT(receive_slot(int)));

    void Qt_ComTest_Client::receive_slot(int result)
{
this->ui.plainTextEdit->appendPlainText("Receive Signal:" + QString::number(result));
}

来尝试一下获取参数:

this->ui.plainTextEdit->appendPlainText(QString(ax_test->property("number").toInt()));

来验证一下:

1.获取参数:

2.点击按钮获得回调:

3.点击控件上的按钮已得到消息:(注,这里需要通过 dynamicCall调用COM控件的show方法来展示界面)

现在就算是完成了整个COM组件的开发,从建立到调用,具体详情我还是建议直接去看Qt的官方文档,不推荐自己一个人琢磨,貌似国内没有什么相关的讨论和研究。

到这里最基本的建立和调用你已经明白了,那么至于我说的服务器模型该怎么实现呢?我将在下文展开讲讲。

Qt的进程间通信,以服务器的形式,手把手教你VS上进行Qt的COM、ActivedQt Server的开发,比保姆还保姆(一)的更多相关文章

  1. 干货!手把手教你如何使用第三方通讯服务实现LayIM Socket组件开发。

    前言 之前写了一系列的文章,是关于使用ASP.NET SignalR技术实现LayIM的功能对接,有兴趣的同学移步:http://www.cnblogs.com/panzi/p/5767095.htm ...

  2. 浏览器上的Qt Quick

    你想不想在浏览器上运行你的Qt Quick程序呢?在Qt 5.12之前,唯一的方法是使用Qt WebGL Streaming技术把界面镜像到浏览器上.但该方法有不少缺陷,下文会说.前不久随着Qt 5. ...

  3. Qt实现客户端与服务器消息发送与文件传输

    Qt实现客户端与服务器消息发送与文件传输需要使用到 QTcpSocket:提供套接字QTcpServer:提供基于TCP的服务端,官方文档的解释如下: This class makes it poss ...

  4. 手把手教你把web应用丢到服务器上(单页应用+ 服务端渲染)

    前两篇文章中,我分别介绍了框架的搭建利用vue-cli + vant搭建一个移动端开发模板,并且把项目中axios请求和vuex的用法做了简要的介绍如何在项目里管理好axios请求与vuex.在这两篇 ...

  5. netty系列之:来,手把手教你使用netty搭建一个DNS tcp服务器

    目录 简介 搭建netty服务器 DNS服务器的消息处理 DNS客户端消息请求 总结 简介 在前面的文章中,我们提到了使用netty构建tcp和udp的客户端向已经公布的DNS服务器进行域名请求服务. ...

  6. 手把手教你认识并搭建Nginx

    手把手教你认识并搭建Nginx Nginx (“engine x”) 是一个高性能的 HTTP 和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器. Nginx 是由 Igor ...

  7. [原创]手把手教你写网络爬虫(7):URL去重

    手把手教你写网络爬虫(7) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 本期我们来聊聊URL去重那些事儿.以前我们曾使用Python的字典来保存抓取过的URL,目的是将重复抓取的UR ...

  8. 手把手教你搭建织女星开发板RISC-V开发环境

    前言 Windows环境下搭建基于Eclipse + RISC-V gcc编译器的RISC-V开发环境,配合openocd调试软件,可以实现RISC-V内核程序的编译.下载和调试. 准备工作 工欲善其 ...

  9. 自已开发IM有那么难吗?手把手教你自撸一个Andriod版简易IM (有源码)

    本文由作者FreddyChen原创分享,为了更好的体现文章价值,引用时有少许改动,感谢原作者. 1.写在前面 一直想写一篇关于im即时通讯分享的文章,无奈工作太忙,很难抽出时间.今天终于从公司离职了, ...

  10. 手把手教你使用Git管理你的软件代码

    什么是分布式版本控制系统?Git有哪些常用命令?什么是仓库?Git的操作区域包括哪些?Git有哪些常用对象(object)?git rebase和git merge的区别是什么?git reset,g ...

随机推荐

  1. 中国数字化是怎么转型成新范式TOP 50的?

    我不大认可"中国数字化转型成新范式TOP 50"的,确切的说,照着"中国数字化转型新范式TOP 50"做转型,大概率失败,对中国企业数字化转型的帮助甚微 ,尤其 ...

  2. P7361 「JZOI-1」拜神 (字符串)

    题意: 给一个串,\(Q\) 次询问区间 \([l,r]\) 中至少出现两次的子串的最大长度. 写LCT是什么东东 以下做法很经典: 先求出 SA 以及 height 数组,然后按 height 从大 ...

  3. Hive Beeline 命令行参数

    [hadoop@hive ~]$ beeline --help[中文版] The Beeline CLI 支持以下命令行参数: Option Description --autoCommit=[tru ...

  4. SQL优化步骤

    当生产数据量急剧增长后,很多SQL语句可能会开始暴露出性能问题.当面对一个有SQL性能问题的数据库时,应该从何处入手进行系统的分析,使得能够尽快定位到问题SQL处并尽快解决问题? 第一步:查看SQL执 ...

  5. 从源码分析 MGR 的流控机制

    Group Replication 是一种 Shared-Nothing 的架构,每个节点都会保留一份数据. 虽然支持多点写入,但实际上系统的吞吐量是由处理能力最弱的那个节点决定的. 如果各个节点的处 ...

  6. Xray

    Xray基础操作 代理设置 运行xray.exe xray.exe genca 运行后会生成ca.crt和cr.key 浏览器导入证书 设置代理7777端口 第一次启动 xray 之后,当前目录会生成 ...

  7. How to get the return value of the setTimeout inner function in js All In One

    How to get the return value of the setTimeout inner function in js All In One 在 js 中如何获取 setTimeout ...

  8. nginx 通过IP访问项目

    项目新需求,因为是小范围使用的网站,所以不打算配域名,直接通过IP访问当前项目. 环境: LNMP 一键集成环境 当前IP指向的目录 :/home/wwwroot/default/ 但是我的项目.需要 ...

  9. HTML+CSS基础知识(4)简单的广告界面

    文章目录 1.网页实例 1.1 代码 1.2 测试效果 1.网页实例 1.1 代码 css样式 /* 清除页面样式 */ *{ margin:0; padding: 0; } /* 统一页面的样式 * ...

  10. (数据科学学习手札145)在Python中利用yarl轻松操作url

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 大家好我是费老师,在诸如网络爬虫.web应用开发 ...