本篇在博客园地址https://www.cnblogs.com/bbqzsl/p/18252961

本篇内容包括:

win32窗口嵌入Qt UI。反斗玩转signal-slot。最后 通达信 x 微信 x Qt 做手术。

Qt Alien Widget是一种广义的DirectUI。

在UI技术中,DirectUI和Alien Widget的概念有所重叠,但具体实现和应用场景有所不同。为了更好地理解它们之间的关系,让我们先定义清楚这几个概念。

DirectUI

DirectUI 是一种界面绘制技术,主要特点是通过直接绘制UI元素,而不是依赖操作系统提供的原生控件。这种方式有以下几个特点:

  • 跨平台一致性:不依赖操作系统的控件,因此在不同操作系统上可以实现一致的外观和行为。
  • 高性能:直接绘制可以优化绘制路径,减少不必要的开销,从而提高性能。
  • 高自由度:开发者可以完全控制UI元素的绘制和交互,灵活性更高。

Native Widget

Native Widget 是由操作系统提供和管理的控件,这些控件直接使用操作系统的窗口和绘图资源。

  • 系统控件:直接由操作系统管理和绘制,提供与操作系统一致的外观和行为。
  • 平台相关:在不同的操作系统上,控件的行为和外观可能不同。

Alien Widget

Alien Widget 是指那些不依赖于操作系统提供的控件,而是由应用程序自身实现和管理的控件。在Qt中,默认控件(如QPushButton, QLineEdit等)都是Alien Widget。

  • 跨平台一致性:在不同操作系统上可以实现一致的外观和行为。
  • 独立性:不依赖操作系统的控件。
  • 灵活性和定制性:开发者可以完全控制控件的外观和行为。

Qt的Alien Widget和DirectUI的关系

Qt的Alien Widget可以看作是DirectUI的一种实现,因为它们都强调不依赖操作系统的原生控件,通过直接绘制实现跨平台一致性和高自由度。

主要特点:

  1. 绘制机制:Qt的Alien Widget是通过Qt的绘图系统(如QPainter)来进行绘制的,这类似于DirectUI技术中直接绘制的概念。
  2. 跨平台一致性:由于Qt的Alien Widget不依赖于操作系统的控件,它们在不同操作系统上提供了一致的外观和行为。
  3. 高自由度和定制性:开发者可以使用Qt提供的丰富API来自定义控件的外观和行为。

结论

可以认为Qt的Alien Widget是DirectUI技术的一种实现形式。两者都不依赖于操作系统的原生控件,通过直接绘制来实现跨平台一致性和高自由度。Qt的Alien Widget利用Qt框架的强大功能,实现了与DirectUI类似的目标。因此,从这个角度来看,Qt的Alien Widget确实可以被视为一种DirectUI技术。

Qt界面不必一定依赖QApplication::exec才能运行

Native Widget 和 QWindow 在 Qt 的图形用户界面编程中有不同的用途和特性。以下是对它们的详细对比和解释:

Native Widget

  • 定义:Native Widget 是由操作系统提供和管理的控件。这些控件直接使用操作系统的窗口和绘图资源,不依赖于 Qt 的绘图系统。
  • 特性
    • 系统控件:直接由操作系统管理和绘制,提供与操作系统一致的外观和行为。
    • 不依赖 Qt 事件循环:可以在不依赖于 QApplication::exec() 的情况下运行,因为它们由操作系统管理事件循环。
    • 平台相关:在不同的操作系统上,控件的行为和外观可能不同。
  • 使用场景:需要与系统深度集成,或使用特定平台功能时使用。例如嵌入系统控件到 Qt 应用程序中。

QWindow

  • 定义:QWindow 是 Qt 的一个抽象类,表示一个独立的窗口。它提供了一个轻量级的窗口对象,可以用于显示内容,但不提供高级的控件功能。
  • 特性
    • Qt 窗口对象:QWindow 是 Qt 提供的窗口类,可以与 Qt 的绘图系统和事件系统无缝集成。
    • 依赖 Qt 事件循环:QWindow 依赖于 QApplication::exec()QGuiApplication::exec() 来运行事件循环。
    • 跨平台一致性:Qt 提供了一致的 API 和行为,不论运行在什么平台上,Qt 窗口的行为和外观都是一致的。
  • 使用场景:用于创建自定义窗口、渲染 OpenGL/Vulkan 内容或其他需要低级别窗口访问的场景。

比较和联系

  • 事件循环:Native Widget 不依赖 Qt 的事件循环,可以独立于 QApplication::exec() 运行。而 QWindow 需要 Qt 的事件循环才能正常工作。
  • 管理和绘制:Native Widget 由操作系统管理和绘制,提供原生外观和行为。QWindow 由 Qt 管理,可以使用 Qt 的绘图系统来绘制内容。
  • 灵活性和集成:Native Widget 适用于需要直接访问操作系统功能和外观一致性的场景。QWindow 提供更高的灵活性和跨平台一致性,适用于需要使用 Qt 提供的高级功能和自定义绘图的场景。

结论

Native Widget 和 QWindow 是 Qt 中不同层次的组件,分别适用于不同的使用场景。Native Widget 更适合需要深度集成操作系统功能的应用,而 QWindow 则更适合需要使用 Qt 的绘图和事件系统的应用。两者的选择应根据具体的需求和应用场景来决定。


以上是AI提供的分析。

由上述可知,Alien Widget可以作为一种dui方案使用,为保证跨平台一致性,请使用QApplication::exec()。

main()
{
QApplication app;
QMainWindow mainWin;
mainWin.show();
app.exec();
}

QApplication跟QMainWindow为固定定式。这个定式能够保证跨平台 一致性。

但是,在windows平台的软件,就有混合使用UI框架的需求。

所以针对windows平台还有一个特殊解决方案:https://github.com/qtproject/qt-solutions/tree/master/qtwinmigrate。

里面包含了三种迁移方案,分别是QMfcApp,QWinHost,QWinWidget。

QMfcApp是方便QApplication跟QMainWindow定式的使用,将QApplication跟CWinApp整合在一起,CWinApp::Run时绑定QApplication::exec。

QWinHost能够将其它框架(不限于MFC)的OS native window嵌入到QWidget。

QWinWidget能够QWidget嵌入到OS native window,以一个native widget hwnd挂到父hwnd。

native widget是由platforms/plugin/qwindows.dll支持的。代码路径在qtbase/src/plugins/platforms/windows,其中qwindowswindow.cpp实现了native window的WndProc,qWindowsWndProc。并将消息转换QtWindows::WindowsEventType由QWindowsContext::windowsProc进行处理。

通达信用的是qt5.5。

QPlatformWindow, qtbase/src/gui/kernel/qplatformwindow.h

QWindowsWindow, qtbase/src/plugins/platforms/windows/qwindowswindow.h

QWindow, qtbase/src/gui/kernel/qwindow.h

QMainWindow, qtbase/src/widgets/widgets/qmainwindow.h

QWidgetWindow, qtbase/src/widgets/kernel/qwidgetwindow_qpa_p.h

QWidget QSurface QPlatformSurface
QMainWindow QWindow QPlatformWindow
  QWidgetWindow QWindowsWindow
  "yes, i do." "you own me?"

尽管这几个都叫Window,单从名字好容易迷惑,它们不尽是QWindow。QWindow与QPlatformWindow是窗口的两个面,QWindow是跨平台的封装,QPlatformWindow则是底层支持。QWidgetWindow是一个私有类,将QWidget与QWindow能够联系在一起,QWidget从而能够依附在一个底层窗口分派事件。

QApplication::exec只是一个幌,隐藏了许多真相。QWidget分派事件需要的窗口过程是由QPlatformWindow提供支持的,但是Qt事件分派器却依赖QApplication,QApplication是一个QObject,QObject设计成绑定于一个线程上运行。所以使得QWidget依赖于QApplication所绑定的线程。因此一个进程范围内只允许有一个Qt的UI线程,而这个线程跟QApplication绑定在一起。

真相是,只有在QApplication绑定的线程上,QPlatformWindow才能在native窗口的消息循环中正常分派Qt事件。所以运行Qt UI的必要条件是QPlatformWindow,QApplication,并且在同一线程,这个线程上有消息循环。QApplication::exec是充分条件但不是必要的。

QPlatformWindow对应一个系统窗口,在跨平台层对应一个QWindow,其中它的继承类QWidgetWindow可以承载QWidget。QMainWindow是一个QWidget并且自带QWidgetWindow。所以QWindow对应着一个系统窗口。自然地QMainWindow也对应着一个系统窗口,同时成为所有子QWidget的窗口设备根基。迁移解决方案的QWinWidget就是用来替代QMainWindow的位置,为QWidget提供窗口设备根基。

QPlatformWindow由平台相关的qwindows.dll进行支持,在windows平台就是QWindowsWindow。必须依赖QApplication进行Object event filter。而QApplication只能绑定在一个线程上。同一进程同一时间,只能有一个线程上的QPlatformWindow以及Widgets可以正常运行。如果在QApplication之外的线程创建的QPlatformWindow以及Widgets是不能正常分派事件的,当它们需要响应事件的时候,QApplication就会警告“Object event filter cannot be in a different thread.”而不能响应。

下图演示,在两个线程分别创建QWinWidget,只有跟随QApplication同一个线程的QWidget能够正常响应事件。

为了演示,我将通达信君的tdxzdview100.dll插入到微信MM的体内,在微信MM体内分别诞生QWinWidget,QWindow,还有QMainWindow。微信MM体内是个没有被MFC污染的洁净环境。演示过程展示了只要有消息循环,就可以运行qt框架的UI。

接下来就是逆向Qt。

Qt以metaObject行了类型反射之实。

定义

反射(Reflection)是一种程序能够在运行时自我检查和操作其结构和行为的机制。通过反射,程序可以动态地获取类型信息,并操作对象、方法、属性等。

涵盖的操作

反射技术涵盖了以下几类操作:

  1. 类型检查和获取元数据

    • 获取类型信息:通过反射,可以在运行时获取对象的类型信息,如类名、方法、属性、字段等。
    • 检查类型成员:检查类型的成员(如方法、属性、事件、构造函数等)的名称、参数、返回类型等信息。
  2. 动态创建对象

    • 实例化类型:通过反射,可以动态地创建类型的实例,而不需要在编译时指定类型。
  3. 调用方法

    • 动态调用方法:可以在运行时调用对象的方法,而不需要在编译时知道方法的名称。
  4. 访问和修改属性和字段

    • 动态访问属性和字段:在运行时读取或修改对象的属性和字段。
  5. 检索和操作特性(注解)

    • 获取和操作特性(注解):通过反射,可以获取和操作应用在类型、方法、属性等上的特性(如C#中的属性,Java中的注解)。

Meta Object可以看作一个小型数据库,提供类型信息。

metacast负责反射类型转换。

metacall是所有反射操作的接口函数。原型类似于ioctl, fcntl。接受包括方法调用,属性访问等。

对于signal-slot的场合,本篇的metacall专指(QMetaObject::Call == InvokeMetaMethod)。

声明过signal跟slot的方法,本质上就是能够通过反射进行调用的方法,我们需要将QObject具体类的成员方法声明为signal或slot,然后还要用moc生成相关的metaobject代码,才能注册到反射。

signal-slot本质就变成了metacall的传递。connect就是对象的metacall连接成拓扑关系,使得metacall可以按拓扑关系进行传递。

signal-slot字面上局限了想象。connect must from signal to slot。然而connect实质是accept from signal to method which can be slot or signal as well.

我们换一下表达式,signal [, signal, ...] - slot,这样就开阔了。

反射不仅提供了类型信息,也可以修改类型信息。简直就是送上门让人逆向。

下图演示通过metaobject查询类型信息,对象方法

下图演示通过跟踪QObject::connect,收集所有signal-slot。这种方法在各Qt版本中比较通用。比较好忽略版本的差异。

下图演示,通过类型反射直接调用对象的slot方法。

下图演示几种修改signal-slot连接关系的方法。通过dynamic meta,或者修改connection,将qt按钮连接到微信按钮,将qt编辑框连接到微信按钮,通过反射来调用按钮的clicked(),调用编辑框的textChanged(QString)。

对于逆向,不需要编译器,不需要头文件,不需要因为slot而去找代码moc编译QObject。

反射的一个特性就是动态修改,那就直接修改。

插入我的slot到signal。signal-slot定式限制了想象,好像只能有一条出路,我必须要有一个slot,方法类型需要对上signal。我必须要写一个QObject达成一个slot,用moc去生成代码。

但是,正如我在上面转换的定式,signal [,signal,... ] -slot,这样一来,signal的下一跳不一定必要是slot,而可以是signal。

拿QPushButton举例,signal clicked() 连接到一个影子QPushButton的signal clicked(),就可以除去一大票工作。

思路一, 通过dynamic meta。

要是我将QPushButton的metacall修改了signal clicked()的处理,必然导致全部QPushButton不能正常工作。意外地,Qt贴心地为我提供了Dynamic MetaObject,解决了我的烦恼。那么我需要moc编译另一个MetaObject吗?不,我以经将moc丢进垃圾桶了。我们可以直接将QPushButton的MetaObject连根复制一份。正所谓CopyOnWrite。我们只要修改副本的qt_static_metacall,将signal clicked()处理修改即可。最后将副本放到影子QPushButton的Dynamic MetaObject。手术完成。缺点是要将其它signal的处理也在同一个qt_static_metacall中修改,不灵活。另外,像clicked()是在父类QAbstractButton中声明为signal的,当其作为connect的receiver端时,并不是用QPushButton的MetaObject::qt_static_metacall分派,而是用QAbstractButton::qt_static_metacall进行分派。十分不方便。这个可以通过QMetaObjectPrivate::connect方法获知继承树中哪个类的MetaObject被选用。

思路二,通过Connection。

Qt5又一贴心设计,QObject::connect返回的不再是冰冷的bool,取而代之是热气腾腾的Connection。不同版本有所差异。但基本三要素没有变,sender, recver, metacall。That's good. Connection还直接指明了metacall函数指针。思路一的工作都可以直接删减了。一个Connection的method_index是固定,所以自已写一个metacall修改起来就简单得多,单一处理,灵活性大大提高。

思路三,自已跟自已。

经过前两次思路进化后,最后我又注意到,影子QPushButton还必要吗?nai, hitsuyu ga nai. 自已跟自已connect,直接省事。谨记,要将修改操作原子地在sender同一线程上进行,不然sender抢先signal一下,也就小狗追尾巴直到天荒地老了。如果需要代码在自已希望的线程上分派,那么还得依靠一个影子QPushButton绑定一个线程。

思路四,Qt5.5也可以connect到Functor。

虽然,Qt5.7才支持connect到Functor。但是在编程时,应用上面的思路,完全可以实现达到目标。

看啦,就这么简单好玩,祝大家也玩得开心。

本篇到这里,下一篇再见。

逆向WeChat(四,mars)

逆向WeChat(三, EventCenter)

逆向WeChat (二, WeUIEngine)

我还有逆向通达信系列

我还有一个K线技术工具项目KTL可以用C++14进行公式,QT,数据分析等开发。

逆向通达信 x 逆向微信 x 逆向Qt的更多相关文章

  1. [python]沪深龙虎榜数据导入通达信的自选板块,并标注于K线图上

    将沪深龙虎榜数据导入通达信的自选板块,并标注于K线图上 原理:python读取前一次处理完的计算5日后涨跌幅输出的csv文件 文件名前加"[paint]" 安照通达信的画图文件和板 ...

  2. 通达信5分钟.lc5和.lc1文件格式

    一.通达信日线*.day文件    文件名即股票代码    每32个字节为一天数据    每4个字节为一个字段,每个字段内低字节在前    00 ~ 03 字节:年月日, 整型    04 ~ 07 ...

  3. 通达信自动交易软件 z

    1.要善用spy++ 2.不同的控件主要靠GetDlgCtrlID去区分 3.要获得另一个进程的焦点窗口(GetFocus)需要调用AttachThreadInput 4.尽量少用keybd_even ...

  4. 通达信zig函数的python实现

    通达信zig函数的python实现 代码 # coding: utf-8 """ Created on Sat Jan 05 18:53:39 2019 http://w ...

  5. pandas 实现通达信里的MFI

    pandas 实现通达信里的MFI 算法里的关键点: combine()和rolling().sum()方法 combine -- 综合运算, rolling().sum() -- 滚动求和 利用pd ...

  6. MACD底背离选股公式——通达信、同花顺

    {底背离,通达信版.同花顺版} DIFF:=EMA(CLOSE,) - EMA(CLOSE,); DEA:=EMA(DIFF,); MACD:=*(DIFF-DEA); QZQ:=BARSLAST(R ...

  7. 通达信k线颜色设置

    通达信的k线函数没有颜色选项.如果想要画颜色可以使用STICKLINE函数来覆盖当前k线这样也是可以满足需求. 第一步画针 STICKLINE(条件 , L , H , 0 , 0 ) , 颜色; 第 ...

  8. 通达信版F10检索工具下载

    通达信版的F10採用的是维赛特的F10资料. 维赛特的F10资料请前往:http://www.vsatsh.cn/xzzq.aspx  下载. 通达信版的F10检索工具下载地址:http://pan. ...

  9. 通达信金融终端_尘缘整合_V7.12

    http://pan.baidu.com/s/1gvtPO http://pan.baidu.com/s/1xqrk6 通达信金融终端_尘缘整合_V7.12

  10. 从通达信导出文件获取A股所有股票代号名称(至2020年2月24日)

    下文是讲述如何从通达信的输出文件中获得股票信息,如果想用Java爬虫从网页爬取信息请参考:https://www.cnblogs.com/xiandedanteng/p/12808381.html 要 ...

随机推荐

  1. 19、python 脚本

    1.python 安装及配置 下载地址 python2 和 python3 共存安装 2.python 可视化 import turtle turtle.pensize(2) #画一个小圆 turtl ...

  2. 终于搞懂了!原来 Vue 3 的 generate 是这样生成 render 函数的

    前言 在之前的 面试官:来说说vue3是怎么处理内置的v-for.v-model等指令? 文章中讲了transform阶段处理完v-for.v-model等指令后,会生成一棵javascript AS ...

  3. Linux搭建ES集群环境

    搭建ES集群环境 准备 三台服务器 其中一台为主机节点 ES安装自行上传到各个节点home路径下并解压重命名 集群名称:cluster-big-data同一个集群多个节点,集群名称必须相同,节点名称不 ...

  4. CCL 2024 Task7 双任务冠军

    近期参加NLP领域CCL2024评测,现将赛题背景和实现方法分享,推理文本纠错领域的发展. 1.背景信息 随着教育的发展和网络的普及,作文评价的规模越来越大,人工评改作文的成本和效率成为一大难题.为了 ...

  5. Debian中配置NIS:用户账号管理

    1.添加指定gid的组 groupadd -g 1001 upload # 添加了一个指定gid为1001的upload用户 2.添加指定uid的用户,并加入到指定组 useradd -u 1001 ...

  6. linux内核参数调优和Linux实例常用内核网络参数介绍与常见问题处理

    问题1 并发场景下,常常会出现一个进程最大文件句柄数不足的情况,会报如下错误: 24: Too many open files 解决办法 ulimit -a S:表示软限制,超出设定的值会告警. H ...

  7. flask blinker信号

    Flask框架中的信号基于blinker,其主要就是让开发者可是在flask请求过程中定制一些用户行为. pip3 install blinker 1.内置信号 request_started = _ ...

  8. FFmpeg开发笔记(二十三)使用OBS Studio开启RTMP直播推流

    ​OBS是一个开源的直播录制软件,英文全称叫做Open Broadcaster Software,广泛用于视频录制.实时直播等领域.OBS不但开源,而且跨平台,兼容Windows.Mac OS.Lin ...

  9. 鸿蒙极速入门(三)-TypeScript语言简介

    ArkTS是HarmonyOS优选的主力应用开发语言.ArkTS围绕应用开发在TypeScript(简称TS)生态基础上做了进一步扩展,继承了TS的所有特性,是TS的超集.因此,在学习ArkTS语言之 ...

  10. 安装numpy:conda install nampy==1.16 时报错An HTTP error occurred when trying to retrieve this URL.

    安装numpy:conda install nampy==1.16 时报错An HTTP error occurred when trying to retrieve this URL. HTTP e ...