本篇在博客园地址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. 一个支持Sora模型文本生成视频的Web客户端

    大家好,我是 Java陈序员. 最近 Open AI 又火了一把,其新推出的文本生成视频模型 -- Sora,引起了巨大的关注. Sora 目前仅仅只是发布预告视频,还未开放出具体的 API. 今天, ...

  2. WEB服务与NGINX(9)-NGINX作为下载服务器的相关配置

    目录 1. NGINX的目录索引功能 2. NGINX的限速功能 2.1 限制下载速度 2.2 限制单位时间内产生的http请求数 2.3 限制客户端同一时刻的并发连接数 1. NGINX的目录索引功 ...

  3. 密码学—Vigenere破解Python程序

    文章目录 概要 预备知识点学习 整体流程 技术名词解释 技术细节 小结 代码 概要 破解Vigenere需要Kasiski测试法与重合指数法的理论基础 具体知识点细节看下面这两篇文章 预备知识点学习 ...

  4. C语言:如何实现在txt文件中删除超链接、统计单词数量、生成单词列表 (文本流操作并解决乱码)

    1.首先读取原文件内容文本流(包含中英文) 2.删除超链接 3.统计单词数量 4.去除重复单词 读取文件需要自己在文本笔记中保存一个网页,保存为txt文件 注意的是,在这个代码实现过程中,我学到的是如 ...

  5. windows 10安装python2.7

    一.官网下载 1.百度搜索"python"或者点击这个网址下载https://www.python.org/downloads/release/python-2716/ pytho ...

  6. AIRIOT答疑第3期|如何使用物联网平台的可视化组态引擎?

    丰富组件,满足千人千面! AIRIOT物联网低代码平台的可视化组态引擎,具备丰富的可视化看板及组件,满足各类工艺流程图.数据可视化需求.支持三维编辑.图形绘制.图表设计等设计方式,PPT模式设计软件界 ...

  7. 热更学习笔记10~11----lua调用C#中的List和Dictionary、拓展类中的方法

    [10]Lua脚本调用C#中的List和Dictionary 调用还是在上文中使用的C#脚本中Student类: lua脚本: print("------------访问使用C#脚本中的Li ...

  8. AI绘图之Midjourney初体验

    Midjourney (MJ) 使用笔记 最近尝试了 Midjourney 绘图,简单记录下使用流程. 注册及登陆 首先是账号注册和登陆,基本上就是一路下一步,唯一需要注意的是加入MJ频道,具体流程为 ...

  9. C#复杂类型转为QueryString

    使用 visual studio 创建 webapi 项目,并添加 DefaultController.cs,代码如下 public class DefaultController : ApiCont ...

  10. GCD Timer事件的精度

    一.测试环境 iPhoneX 真机+Debug模式,Timer代码工作在主线程,主线程空闲不阻塞 在子线程统计每3秒tick计数,逐步减小inteval,看能达到多大精度. 忽略原子计数值操作的影响 ...