- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
最近我们项目部的核心产品正在进行重构,然后又是年底了,除了开发工作之外项目并不紧急,加上加班时间混不够了....所以就忙里偷闲把整个项目的开发思路聊一下,以供参考.
鉴于接下来的一年我要进行这个主框架的开发,本着精益求精的态度,加上之前维护前辈的产品代码确实给我这个刚毕业的社畜带来了不小的震撼,我决定在这个模块的开发中优化之前的开发模式,提升整个产品的健壮性和独立性.
开发一个大型软件最重要的问题有三个,一是如何保证每个模块开发的独立性 二是如何保证数据结构的一致性 三是如何保证程序的可维护性和健壮性。这几个文章的内容我会在几篇文章中分开聊聊我的做法,做个记录.
本篇文章聊聊如何保证各个模块开发的独立性——怎么让功能模块、教学模块的开发独立于主框架本身。让不同的模块之间尽量通过接口的形式进行交互,而抛弃传统的中转消息码->调用模块的模式,让实际功能以接口形式暴露.
这一期来聊聊开发中遇到的一些问题:QtActive Server如何通过COM口传递自定义结构体?如何通过一个COM口来获得所有COM接口?
浏览本文前,请务必查看前置文章以获得更好的阅读体验,避免你不知道我在说什么:
Qt开发Active控件:如何使用ActiveQt Server开发大型软件的主框架 。
Qt开发:Windows 下进程间通信的可行桥梁:窗体消息 。
【大型软件开发】浅谈大型Qt软件开发(一)开发前的准备——在着手开发之前,我们要做些什么?
就我在开发的过程中发现了一个问题,就我的命名格式是以类似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软件开发(三)QtActiveServer如何通过COM口传递自定义结构体?如何通过一个COM口来获得所有COM接口?的文章就讲到这里了,如果你想了解更多关于【大型软件开发】浅谈大型Qt软件开发(三)QtActiveServer如何通过COM口传递自定义结构体?如何通过一个COM口来获得所有COM接口?的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
COM 内存泄漏最常见的原因是什么? 我读过将初始化的 CComBSTR 的地址作为 [out] 参数传递给函数会导致泄漏。我正在寻找像这样枚举其他常见的编程错误。 最佳答案 未能为 COM 对象使用
在COM服务器执行过程中分配一 block 内存,然后通过一个输出参数将该内存块传递给客户端是很常见的。然后,客户端有义务使用 CoTaskMemFree() 等方法释放该内存。 问题是,这 bloc
我有一些 MFC 代码(自定义 CWnd 控件和一些要公开的类),我需要将它们制作成带有接口(interface)的 activex/COM 对象。使用 MFC 支持制作 ATL 项目并以这种方式制作
Devenv.com 是 visual studio 命令行界面,当您键入 devenv/? 时,devenv 的帮助会出现在控制台上。但是,如果没有任何选项,devenv.com 只会调用 deve
如何将 COM 接口(interface)的引用作为 COM 库中的参数传递? 这是示例: 1)客户端代码成功创建coclass并接收到pFunctionDiscovery中的接口(interface
我正在使用 django,我在 s3 中存储了诸如 imgs 之类的东西(为此我使用的是 boto),但最近我收到了这个错误: 'foo.bar.com.s3.amazonaws.com' doesn
我已经使用组件服务 MSC 对话框创建了一个 COM+ 应用程序。我将一个现有的 COM 对象导入到这个新的 COM+ 应用程序中。 我知道可以通过 COM+ 应用程序调用该 COM 对象。我可以简单
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 关闭 11 年前。 Improve thi
我正在使用通过 COM Interop 包装器公开的第三方 dll。但是,其中一个 COM 调用经常卡住(至少从不返回)。为了至少让我的代码更健壮一些,我异步包装了调用(_getDeviceInfoW
很多年前我读到有一个简单的 php 脚本可以将您的网站重定向到 http://example.com/google.com 到 google.com它适用于正斜杠右侧的任何域。我忘记了这个脚本是什么或
我正在实现我的第一个进程外 COM 服务器(我的第一个 COM 服务器,就此而言)。我已经按照步骤编写了一个 IDL 文件,为代理/ stub DLL 生成代码,编译 DLL,并注册它。 当我检查注册
是否可以在未知接口(interface)上增加 RCW 引用计数? (即不是底层 COM 对象的引用计数) 我有一些旧的 COM 服务器代码 int Method1(object comobject)
我注意到许多关于 COM 的书籍等都指出,在 COM 聚合中实现一个可用作内部对象的对象相对容易。但是,除非我遗漏了什么,否则聚合似乎只能在极其有限的场景中成功,因此只有在明确识别出这种场景时才应提供
假设我正在开发一个安装 COM 组件并安装程序注册它们的应用程序。这很好用。 现在该软件需要从内存棒上运行。如何注册我的库运行时并确保在运行应用程序后清理注册表? 最佳答案 您总是在 XP 或更高版本
我们已经使用Microsoft的ActiveX/COM(VB6)技术开发了一个软件系统。去年,我对自动化构建过程和整个SCM越来越感兴趣。我集中搜索了网络的大部分内容,以获取有关如何使用基于COM的软
我对 com 线程模型有点困惑。 我有一个 inproc 服务器,我想创建一个可从任何线程访问的接口(interface),而不管 CoInitializeEx 中使用的线程模型和/或标志。 当将接口
我的包以旁加载方式安装,并不断遇到特定于应用程序的权限错误。 是的,许多人建议在 regedit 和组件服务中手动更改权限和所有者。 我的应用实际上在组件服务(DCOMCNFG、DCOMCNFG -3
我正在使用第三方应用程序,并调用创建 的实例。我的 COM 对象。这个调用成功了,但是第三方应用程序上的函数没有返回指向创建对象的指针(我不知道为什么)。有没有办法获得指向我的对象的指针? 为了澄清,
我有一个用 C# 编写的托管 COM 对象和一个用 C++(MFC 和 ATL)编写的 native COM 客户端和接收器。客户端创建对象并在启动时向其事件接口(interface)提供建议,并在其
我的应用程序需要注册两个 COM DLL。如果用户有必要的访问权限,它会自动完成,否则可以使用 regsvr32 完成。 . 现在在一些工作站上会发生以下情况: 开始cmd.exe作为管理员 注册第一
我是一名优秀的程序员,十分优秀!