前言

最近我们项目部的核心产品正在进行重构,然后又是年底了,除了开发工作之外项目并不紧急,加上加班时间混不够了....所以就忙里偷闲把整个项目的开发思路聊一下,以供参考。

鉴于接下来的一年我要进行这个主框架的开发,本着精益求精的态度,加上之前维护前辈的产品代码确实给我这个刚毕业的社畜带来了不小的震撼,我决定在这个模块的开发中优化之前的开发模式,提升整个产品的健壮性和独立性。

开发一个大型软件最重要的问题有三个,一是如何保证每个模块开发的独立性 二是如何保证数据结构的一致性 三是如何保证程序的可维护性和健壮性。这几个文章的内容我会在几篇文章中分开聊聊我的做法,做个记录。

本篇文章聊聊如何保证各个模块开发的独立性——怎么让功能模块、教学模块的开发独立于主框架本身。让不同的模块之间尽量通过接口的形式进行交互,而抛弃传统的中转消息码->调用模块的模式,让实际功能以接口形式暴露。

这一期来聊聊开发中遇到的一些问题:QtActive Server如何通过COM口传递自定义结构体?如何通过一个COM口来获得所有COM接口?

浏览本文前,请务必查看前置文章以获得更好的阅读体验,避免你不知道我在说什么:

Qt开发Active控件:如何使用ActiveQt Server开发大型软件的主框架

Qt开发:Windows 下进程间通信的可行桥梁:窗体消息

【大型软件开发】浅谈大型Qt软件开发(一)开发前的准备——在着手开发之前,我们要做些什么?

一、如何让ActiveServer的接口以树形结构暴露

就我在开发的过程中发现了一个问题,就我的命名格式是以类似sig_SeatManager_GetAllSeatInfo()这样的方式命名的,虽然看上去结构清晰,但是总的来说不够简洁。在面对长时间的开发和维护,一个COM类暴露的接口和信号可能直接多达上百个,这显然是极大的影响了程序的维护效率。也就是类似图下:几乎所有的功能模块都通过Kernel 去调用了,这显然是不合适的。

最好的情况肯定是:我们所有的功能都通过每个功能模块的单例去调用。然后每个暴露的接口都是根据各个不同的类分门别类来处理功能的,也就是说,每个类都能有一个自己单独的暴露COM接口的类型,Interface_Kernel类只需要提供向各个接口类的重定向就好。

那么怎么做呢?其实也很简单,COM接口除了最基本的数据类:

其实还可以直接传递指针,

注:这个似乎不一定要使用Q_PROPERTY注册相关的属性,当然了也不一定,需要自己去测试一下,我反正写完了我就懒得管了

Q_PROPERTY(SeatManager* GetSeatManager1 READ GetSeatManager)
SeatManager* GetSeatManager() {
this->test = &SeatManager::Singleton();
return this->test;
}

在调用方就可以这样调用:

Interface = new QAxObject();

if (!Interface->setControl("LBD_VS19.ILBD_CloudNetIntelClassroom.1")) {
//获取失败
this->Add("COM Interface Load Failed! Check ActiveQt Server is Exist.");
}
//用于获取SeatManager的指针
QAxObject* Interface2 = new QAxObject();
Interface2 = Interface->querySubObject("GetSeatManager1"); //获取SeatManager类的接口文档
QFile docs2("AX_Interfaces.html");
docs.open(QIODevice::ReadWrite | QIODevice::Text);
QTextStream TS2(&docs);
TS << str_interfaces << endl;

另外需要注意的一点是,这个SeatManager也需要在开头声明以下宏:

Q_OBJECT
Q_CLASSINFO("ClassID", "{2642F93D-069A-420C-A309-5E4F1808320B}")
Q_CLASSINFO("InterfaceID", "{20F4EA3B-A8AD-42C0-8AAA-1C97F1BD35CD}")
Q_CLASSINFO("EventsID", "{3C1458B9-C236-48BF-A9C0-2BEB0221C173}")
Q_CLASSINFO("RegisterObject", "yes")

但是这个SeatManager不需要继承QAxBindable类,因为这个类需要提供功能但是并不是直接对外暴露给系统去调用的。由上就可以通过一个接口将几乎所有的接口类全部通过COM接口及文档的方式暴露给客户,以供调用。

二、如何传递自定义结构体或者类

这个在网上也是没说,Qt的官方文档写的也是一坨稀烂,报的相关错误更是重量级。

一开始我想的是直接通过QVarirant类直接将我的自定义类型转换一下,比如类似使用Q_DECLARE_METATYPE(test_struct)这样的宏直接进行转换。但是在我多次尝试之后一直会报错

QAxBase: Error calling IDispatch member getvseat_info: Type mismatch in parameter -1

后来我才意识到,这样的数据可以在一个进程内部自由流动,但是QVariant定义了一个自定义结构是不能直接在COM接口之间自由流动的,这部分需要去稍微了解一下COM的定义及内部结构才能更好的明白,总之你只需要知道并不能在ActiveServer这边定义一个接口,然后在调用方去直接获得这个QVariant对象,然后再强制转换回来,这样的操作是非法的。

怎么做?

其实我们能有一个相当简单粗暴的方式,也是一个可以体现cpp优越性的方式:直接强行把对象转成二进制流,然后通过COM口返回,再让调用方去转换这个二进制流。

我们来看下代码,其实比较简单:

ActiveServer:

//在此转换结构体
QByteArray myStructMethod() {
QByteArray send;
send.resize(sizeof(test_struct)); std::memcpy(send.data(), &testinstance, sizeof(test_struct)); return send;
}

调用方:

resultarr = Interface->dynamicCall("getseat_info()").toByteArray();
SeatInfo* tseatinfo = (SeatInfo*)resultarr.data();

这样就是相当于把在ActiveServer中的一个类直接转换成QByteArray,然后发送给调用方去转换这个QByteArray

这个做法和Json的方式比,有优点也有缺点

优点:

使用方便,只需要两边有对其的头文件就可以直接转换类或者结构体,直接跨线程无损传递数据,比JSON方便得多还少很多步骤

缺点:

1.几乎是不可维护的,因为两边的类类型必须对齐,也就是说两边的数据类型都完全无法二次加工,最好是只存放数据,如果需要自定义的化只能自己新开一个类。

2.不同的语言之间不能协调,因为我们原来的这个类是继承了QObject类,如果我们换一种语言用不到QObejct,那么这个类就变成不可获取的接口了。

注:

尽管缺点非常明显,我们还是选择了使用此方式。

1.因为QObject类可以提供QJson和QObject的转换--详情看我的轮子QJson和QObject的转换--轮子,在面对不同语言时其实struct的兼容性也并不好,所以思来想去还是直接传指针算了,除了指针之外还需要另外提供一套Json的方法,以供一些非Qt的教学模块以及第三方的进程使用---并不是只有我们内部使用的东西,我们只提供JSON字符串!

2.不得不说,这样做可以极大的减少Qt开发子模块的工作量,也是我们主要重做这个框架的重要目的之一。

【大型软件开发】浅谈大型Qt软件开发(三)QtActive Server如何通过COM口传递自定义结构体?如何通过一个COM口来获得所有COM接口?的更多相关文章

  1. Qt 信号槽传递自定义结构体

    Qt 在信号和槽中使用自己定义的结构体

  2. [IC]浅谈嵌入式MCU软件开发之中断优先级与中断嵌套

    转自:https://mp.weixin.qq.com/s?__biz=MzI0MDk0ODcxMw==&mid=2247483680&idx=1&sn=c5fd069ab3f ...

  3. 浅谈大型web系统架构

    动态应用,是相对于网站静态内容而言,是指以c/c++.php.Java.perl..net等服务器端语言开发的网络应用软件,比如论坛.网络相册.交友.BLOG等常见应用.动态应用系统通常与数据库系统. ...

  4. 转:浅谈大型web系统架构

    浅谈大型web系统架构 动态应用,是相对于网站静态内容而言,是指以c/c++.php.Java.perl..net等服务器端语言开发的网络应用软件,比如论坛.网络相册.交友.BLOG等常见应用.动态应 ...

  5. 浅谈OA办公软件市场行情

    3.原文:http://www.jiusi.net/detail/472__776__3999__1.html 关键词:oa系统,OA办公软件 浅谈OA办公软件市场行情 中国的OA办公软件市场历经20 ...

  6. 【ZZ】浅谈大型web系统架构 | 菜鸟教程

    浅谈大型web系统架构 http://www.runoob.com/w3cnote/large-scale-web-system-architecture.html

  7. 浅谈关于QT中Webkit内核浏览器

    关于QT中Webkit内核浏览器是本文要介绍的内容,主要是来学习QT中webkit中浏览器的使用.提起WebKit,大家自然而然地想到浏览器.作为浏览器内部的主要构件,WebKit的主要工作是渲染.给 ...

  8. springboot开发浅谈 2021/05/11

    学习了这么久,本人希望有时间能分享一下,这才写下这篇浅谈,谈谈软件,散散心情. 这是本人的博客园账号,欢迎关注,一起学习. 一开始学习springboot,看了好多网站,搜了好多课程.零零落落学了一些 ...

  9. [原创]浅谈如何使用gcc开发NT核心驱动程序

    原文链接:[原创]浅谈如何使用gcc开发NT核心驱动程序 一谈到在 Win NT 下开发核心驱动程序,可能不少人首先都会想到微软“正统”的VC来.诚然,用VC 配合 WINDDK 的确工作的不错,但或 ...

  10. 【WebApi系列】浅谈HTTP在WebApi开发中的运用

    WebApi系列文章 [01]浅谈HTTP在WebApi开发中的运用 [02]聊聊WebApi体系结构 [03]详解WebApi参数的传递 [04]详解WebApi测试和PostMan [05]浅谈W ...

随机推荐

  1. 支持向量机(SVM)公式整理

    支持向量机可以分为三类: 线性可分的情况 ==> 硬间隔最大化 ==> 硬间隔SVM 近似线性可分的情况 ==> 软间隔最大化 ==> 线性支持向量机 线性不可分的情况 ==& ...

  2. UE优化性能

    UE 优化 参考:风恋残雪的博客 Stat unit 启动一个非Debug的游戏进程 打开控制台输入 Stat UNIT .PC端 ` , Android 四指点击 正常的渲染状态: 名称 功能 Fr ...

  3. gorm

    特性 全功能 ORM 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承) Create,Save,Update,Delete,Find 中钩子方 ...

  4. JUC学习笔记——共享模型之内存

    JUC学习笔记--共享模型之内存 在本系列内容中我们会对JUC做一个系统的学习,本片将会介绍JUC的内存部分 我们会分为以下几部分进行介绍: Java内存模型 可见性 模式之两阶段终止 模式之Balk ...

  5. JAVA系列之类加载机制详解

    类的加载机制 ? 双亲委派机制 ? 什么是类加载器 ? 自定义类加载器有哪些应用场景 ? 通常,在关于Java的类加载部分会遇到以上疑问,本文将对类加载重要部分做详细介绍,包括重要的基础概念和应用场景 ...

  6. ssh免交互

    sshpass -p the_password ssh -o StrictHostKeyChecking=no root@domainname_or_ip remote_command  #远程执行命 ...

  7. liunx下安装Jexus5.8.2独立版运行asp.net MVC5

    Jexus 5.8.2独立版介绍 支持系统(64位):CentOS 6.5.Ubuntu 12.04版本以上包含当前版本. 运行环境:WebForm.Mvc3-5.WebService WebApi, ...

  8. 一张VR图像帧的生命周期

    "VR 应用程序每帧渲染两张图像,一张用于左眼,一张用于右眼."人们通常这样来解释 VR 渲染,虽然没有错,但可能过于简单化了.对于 Quest 开发人员来说,了解全貌是有益的,这 ...

  9. .NET 6使用ImageSharp给图片添加水印

    ​ .NET 6 中,使用System.Drawing操作图片,生成解决方案或打包的时候,会有警告,意思是System.Drawing仅在 'windows' 上受支持.微软官方的解释是: Syste ...

  10. SQL注入绕waf思路总结

    1.关键字大小写混合绕过 关键字大小写混合只针对于小写或大写的关键字匹配技术-正则表达式,如果在匹配时大小写不敏感的话,就无法绕过.这是最简单的一个绕过技术. 例如:将union select混写成U ...