当前位置: 首页 > article >正文

在MFC中使用Qt(六):深入了解QMfcApp

前言

此前系列文章回顾:
在MFC中使用Qt(一):玩腻了MFC,试试在MFC中使用Qt!(手动配置编译Qt)

在MFC中使用Qt(二):实现Qt文件的自动编译流程

在MFC中使用Qt(三):通过编辑项目文件(.vcxproj)实现Qt的自动化编译流程

在MFC中使用Qt(四):使用属性表(Property Sheet)实现自动化Qt编译流程

在MFC中使用Qt(五):MFC和Qt的共存和交互

通过源码qt-solutions开源项目可知QMfcApp 通过以下几个关键技术点实现 MFC 和 Qt 事件循环的共存:
1.使用Windows钩子来监控消息队列,确保Qt事件在MFC的消息循环中被处理。
2.重写事件过滤器(winEventFilter)来处理Windows消息。
3.在Qt的事件循环中集成MFC的消息泵,通过定期处理MFC的Onldl或消息分发。
4.管理模态循环的计数,调整Qt的事件处理行为,避免在模态期间出现问题。
5.静态方法run0和instance0帮助初始化和协调两个框架的事件循环。

本文将详细说明其工作原理。

深入了解QMfcApp

Windows钩子(Hook)确保事件同步

关键代码:

// 安装 WH_GETMESSAGE 钩子监控消息队列
hhook = SetWindowsHookEx(WH_GETMESSAGE, QtFilterProc, 0, GetCurrentThreadId());LRESULT CALLBACK QtFilterProc(...) {// 强制刷新 Qt 事件队列,防止事件积压qApp->sendPostedEvents();return CallNextHookEx(...);
}

作用:
在每次 Windows 消息被取出(GetMessage 或 PeekMessage)时,触发钩子回调,确保 Qt 的 DeferredDelete 等事件及时处理。

​避免阻塞:即使 MFC 窗口模态对话框阻塞了 Qt 主循环,钩子仍能保证 Qt 事件被处理。

重写事件过滤器(winEventFilter)

关键代码:

bool QMfcApp::winEventFilter(MSG *msg, long *result)
{static bool recursion = false;if (recursion)return false;recursion = true;QWidget *widget = QWidget::find((WId)msg->hwnd);HWND toplevel = 0;if (widget) {HWND parent = (HWND)widget->winId();while(parent) {toplevel = parent;parent = GetParent(parent);}HMENU menu = toplevel ? GetMenu(toplevel) : 0;if (menu && GetFocus() == msg->hwnd) {if (msg->message == WM_SYSKEYUP && msg->wParam == VK_MENU) {// activate menubar on Alt-up and move focus awaySetFocus(toplevel);SendMessage(toplevel, msg->message, msg->wParam, msg->lParam);widget->setFocus();recursion = false;return TRUE;} else if (msg->message == WM_SYSKEYDOWN && msg->wParam != VK_MENU) {SendMessage(toplevel, msg->message, msg->wParam, msg->lParam);SendMessage(toplevel, WM_SYSKEYUP, VK_MENU, msg->lParam);recursion = false;return TRUE;}}}
#ifdef QTWINMIGRATE_WITHMFCelse if (mfc_app) {MSG tmp;while (doIdle && !PeekMessage(&tmp, 0, 0, 0, PM_NOREMOVE)) {if (!mfc_app->OnIdle(idleCount++))doIdle = FALSE;}if (mfc_app->IsIdleMessage(msg)) {doIdle = TRUE;idleCount = 0;}}if (mfc_app && mfc_app->PreTranslateMessage(msg)) {recursion = false;return TRUE;}
#endifrecursion = false;
#if QT_VERSION < 0x050000return QApplication::winEventFilter(msg, result);
#elseQ_UNUSED(result);return false;
#endif
}
  • 作用QMfcApp::winEventFilter 是 Qt 与 MFC 消息协调的核心枢纽,其核心目标是 ​优先让 MFC 处理关键消息(如菜单、快捷键、空闲任务),剩余消息交由 Qt 处理
  • 处理逻辑
    • winEventFilter 中优先处理 MFC 消息(如 WM_ENTERIDLE),调用 MFC 的 OnIdle 等接口。
    • 剩余消息交给 Qt 的标准处理流程 QApplication::winEventFilter()

Qt 主循环驱动 MFC 消息泵

关键代码:

// 伪代码:Qt 主循环内部逻辑
int QApplication::exec() {while (!exit_loop) {// 处理 Qt 事件processEvents(); // 嵌入 MFC 消息泵逻辑while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {TranslateMessage(&msg);DispatchMessage(&msg); // MFC 窗口处理消息}}return exit_code;
}

关键点
Qt 的 exec() 并非单纯阻塞,其内部会通过 ​非阻塞轮询(PeekMessage)​ 持续检查并分发 Windows 消息。

  • PeekMessage 代替了 MFC 的 PumpMessage,允许在 Qt 主循环中处理 MFC 窗口消息。
  • MFC 无需独立事件循环:所有消息最终由 DispatchMessage 分发到 MFC 窗口过程(如 CWnd::WindowProc)。

模态循环协同

关键代码:

// 进入/退出模态循环时更新计数器
void QMfcApp::enterModalLoop() { ++modalLoopCount; }
void QMfcApp::exitModalLoop()  { --modalLoopCount; }

作用:在 MFC 模态对话框(如 MessageBox)期间调整 Qt 事件处理策略。

逻辑细节

  • 进入模态时暂停 Qt 的 DeferredDelete 事件,防止对象在模态期间被销毁。
  • 退出时恢复事件处理,确保 UI 状态一致。

绕过MFC 的 CWinApp::Run

QMfcApp 的设计中,​MFC 的消息循环并未独立启动,而是通过 ​将 MFC 消息泵嵌入到 Qt 的主事件循环 中实现共存。

// 典型 MFC 应用的主循环(被 QMfcApp 替换)
int CWinApp::Run() {while (PumpMessage()) {} // 传统 MFC 消息泵return ExitInstance();
}// QMfcApp 的替代实现
int QMfcApp::run(CWinApp *mfcApp) {qApp->exec();          // 启动 Qt 主循环(内含 MFC 消息泵)mfcApp->ExitInstance(); // 清理 MFC 资源
}

关键设计

  • 不调用 CWinApp::Run:QMfcApp 完全接管消息循环,避免 MFC 自身循环启动。
  • 资源清理:Qt 主循环退出后,手动调用 ExitInstance 完成 MFC 清理。

总结

QMfcApp ​未同时运行两个独立事件循环,而是通过以下方式实现协作而非并行:

  1. Qt 主循环驱动:通过 PeekMessage 处理所有消息(包括 MFC 窗口消息)。
  2. MFC 逻辑嵌入:消息过滤器和钩子确保 MFC 的 PreTranslateMessageOnIdle 等关键逻辑被触发。
  3. 无阻塞设计:Qt 的 exec() 内部以非阻塞方式轮询消息,避免独占线程。

这种设计使得 MFC 窗口能够响应消息,而 Qt 控件也能正常更新,实现无缝混合运行。

相关文章:

在MFC中使用Qt(六):深入了解QMfcApp

前言 此前系列文章回顾&#xff1a; 在MFC中使用Qt&#xff08;一&#xff09;&#xff1a;玩腻了MFC&#xff0c;试试在MFC中使用Qt&#xff01;&#xff08;手动配置编译Qt&#xff09; 在MFC中使用Qt&#xff08;二&#xff09;&#xff1a;实现Qt文件的自动编译流程 在M…...

JMeter进行分布式压测

从机&#xff1a; 1、确认防火墙是否关闭&#xff1b; 2、打开网络设置&#xff0c;关闭多余端口&#xff1b;&#xff08;避免远程访问不到&#xff09; 3、打开JMeter/bin 目录底下的jmeter.properties&#xff1b; remove_hosts设置当前访问地址&#xff0c;192.XXXXX&…...

Python实现音频数字水印方法

数字水印技术可以将隐藏信息嵌入到音频文件中而不明显影响音频质量。下面我将介绍几种在Python中实现音频数字水印的方法。 方法一&#xff1a;LSB (最低有效位) 水印 import numpy as np from scipy.io import wavfile def embed_watermark_lsb(audio_path, watermark, ou…...

快速入手-基于Django-rest-framework的第三方认证插件(SimpleJWT)权限认证扩展返回用户等其他信息(十一)

1、修改serializer.py&#xff0c;增加自定义类 # 自定义用户登录token等返回信息 class MyTokenObtainPair(TokenObtainPairView): def post(self, request, *args, **kwargs): serializer self.get_serializer(datarequest.data) try: serializer.is_valid(raise_exceptio…...

关于IP免实名的那些事

IP技术已成为个人与企业保护隐私、提升网络效率的重要工具。其核心原理是通过中介服务器转发用户请求&#xff0c;隐藏真实IP地址&#xff0c;从而实现匿名访问、突破地域限制等目标。而“免实名”代理IP的出现&#xff0c;进一步简化了使用流程&#xff0c;用户无需提交身份信…...

【SQL性能优化】预编译SQL:从注入防御到性能飞跃

&#x1f525; 开篇&#xff1a;直面SQL的"阿喀琉斯之踵" 假设你正在开发电商系统&#x1f6d2;&#xff0c;当用户搜索商品时&#xff1a; -- 普通SQL拼接&#xff08;危险&#xff01;&#xff09; String sql "SELECT * FROM products WHERE name "…...

Spring容器从启动到关闭的注解使用顺序及说明

Spring容器从启动到关闭的注解使用顺序及说明 1. 容器启动阶段 注解&#xff1a;Configuration、ComponentScan 作用&#xff1a; Configuration&#xff1a;标记配置类&#xff0c;声明Spring应用上下文的配置源。ComponentScan&#xff1a;扫描指定包下的组件&#xff08;B…...

UVM概念面试题100问

1-10:UVM概述 Q1: 什么是UVM? A1: UVM是Universal Verification Methodology的缩写,它是由Accellera标准化的一种用于IC验证的方法学。它提供了一个基类库(BCL),包含通用工具如组件层次结构、事务级模型(TLM)和配置数据库等,使用户能够创建结构化、可重用的验证环境。 Q2:…...

SQL Server从安装到入门一文掌握应用能力。

本篇文章主要讲解,SQL Server的安装教程及入门使用的基础知识,通过本篇文章你可以快速掌握SQL Server的建库、建表、增加、查询、删除、修改等基本数据库操作能力。 作者:任聪聪 日期:2025年3月31日 一、SQL Server 介绍: SQL Server 是微软旗下的一款主流且优质的数据库…...

力扣HOT100之矩阵:54. 螺旋矩阵

这道题之前在代码随想录里刷过类似的&#xff0c;还有印象&#xff0c;我就按照当初代码随想录的思路做了一下&#xff0c;结果怎么都做不对&#xff0c;因为按照代码随想录的边界条件设置&#xff0c;当行数和列数都为奇数时&#xff0c;最后一个元素无法被添加到数组中&#…...

5.1 WPF路由事件以及文本样式

一、路由事件 WPF中存在一种路由事件&#xff08;routed event&#xff09;&#xff0c;该事件将发送到包含该控件所在层次的所有控件&#xff0c;如果不希望继续向更高的方向传递&#xff0c;只要设置e.Handled true即可。 这种从本控件-->父控件->父的父控件的事件&am…...

Python数据可视化-第1章-数据可视化与matplotlib

环境 开发工具 VSCode库的版本 numpy1.26.4 matplotlib3.10.1 ipympl0.9.7教材 本书为《Python数据可视化》一书的配套内容&#xff0c;本章为第1章 数据可视化与matplotlib 本文主要介绍了什么是数据集可视化&#xff0c;数据可视化的目的&#xff0c;常见的数据可视化方式…...

Flutter敏感词过滤实战:基于AC自动机的高效解决方案

Flutter敏感词过滤实战&#xff1a;基于AC自动机的高效解决方案 在社交、直播、论坛等UGC场景中&#xff0c;敏感词过滤是保障平台安全的关键防线。本文将深入解析基于AC自动机的Flutter敏感词过滤实现方案&#xff0c;通过原理剖析实战代码性能对比&#xff0c;带你打造毫秒级…...

20250331-vue-组件事件1触发与监听事件

触发与监听事件 1 在组件的模板表达式中&#xff0c;可以直接使用 $emit 方法触发自定义事件(例如&#xff1a;在 v-on 的处理函数中)&#xff1a; 子组件代码 <template><button click"$emit(someEvent)">点击</button> </template><…...

Odoo/OpenERP 和 psql 命令行的快速参考总结

Odoo/OpenERP 和 psql 命令行的快速参考总结 psql 命令行选项 选项意义-a从脚本中响应所有输入-A取消表数据输出的对齐模式-c <查询>仅运行一个简单的查询&#xff0c;然后退出-d <数据库名>指定连接的数据库名&#xff08;默认为当前登录用户名&#xff09;-e回显…...

Vue中使用antd-table组件时,树形表格展开配置不生效-defaultExpandedRowKeys-默认展开配置不生效

defaultExpandedRowKeys属性 defaultExpandAllRows这个属性仅仅是用来设置默认值的,只在第一次渲染的时候起作用,后续再去改变,无法实现响应式 解决方案一 a-table表格添加key属性,当每次获取值时,动态改变key,以达到重新渲染的效果 <a-table:key="tableKey"…...

VRRP交换机三层架构综合实验

题目要求&#xff1a; 1&#xff0c;内网Ip地址使用172.16.0.0/16分配 说明可以划分多个子网&#xff0c;图中有2个VLAN&#xff0c;可以根据VLAN划分 2&#xff0c;sw1和SW2之间互为备份 互为备份通常通过VRRP&#xff08;虚拟路由冗余协议&#xff09;来实现。VRRP会在两个…...

基于卷积神经网络的眼疾识别系统,resnet50,efficentnet(pytorch框架,python代码)

更多图像分类、图像识别、目标检测、图像分割等项目可从主页查看 功能演示&#xff1a; 眼疾识别系统resnet50&#xff0c;efficentnet&#xff0c;卷积神经网络&#xff08;pytorch框架&#xff0c;python代码&#xff09;_哔哩哔哩_bilibili &#xff08;一&#xff09;简介…...

基于srpingboot智慧校园管理服务平台的设计与实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…...

【力扣hot100题】(026)合并两个有序链表

可以创建一个新链表记录答案&#xff1a; /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *…...

TCP网络编程与多进程并发实践

一、引言 在网络编程中&#xff0c;TCP&#xff08;传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议。而多进程并发则是一种提高服务器处理能力的有效手段&#xff0c;允许服务器同时处理多个客户端的请求。本文将详细介绍如何使用 TCP 协议进…...

【前端】一文掌握 Vue 3 指令用法(vue3 备忘清单)

文章目录 入门介绍创建应用应用实例通过 CDN 使用 Vue使用 ES 模块构建版本模板语法文本插值原始 HTMLAttribute 绑定布尔型 Attribute动态绑定多个值使用 JavaScript 表达式仅支持表达式(例子都是无效)调用函数指令 Directives参数 Arguments绑定事件动态参数动态的事件名称修…...

visio导出pdf公式变形

情况描述导出为pdf后&#xff0c;mathtype写的公式就变形了 但是导出为png和jpg就是正常 解决方法就是 需要下载一个Adobe Acrobat...

【学习笔记】计算机网络(六)

第6章应用层 文章目录 第6章应用层6.1 域名系统DNS6.1.1 域名系统概述6.1.2 互联网的域名结构6.1.3 域名服务器域名服务器的分区管理DNS 域名服务器的层次结构域名服务器的可靠性域名解析过程-两种查询方式DNS 高速缓存机制 6.2 文件传送协议6.2.1 FTP 概述6.2.2 FTP 的基本工作…...

做一个多级动态表单,可以保存数据和回显数据

<template> <div class"two"> <button class"save" click"saveBtn">保存数据</button> <button class"sd" click"showBtn">回显数据</button> <div class"all" click&quo…...

量子退火与机器学习(2):少量实验即可找到新材料,黑盒优化➕量子退火

使用量子退火和因子分解机设计新材料 这篇文章是东京大学的一位博士生的毕业论文中的主要贡献。 结合了黑盒优化和量子退火&#xff0c;是融合的非常好的一篇文章&#xff0c;在此分享给大家。 https://journals.aps.org/prresearch/abstract/10.1103/PhysRevResearch.2.0133…...

WPF中的Adorner基础用法详解与实例

WPF中的Adorner基础用法详解与实例 Adorner&#xff08;装饰器&#xff09;是WPF中一个强大的功能&#xff0c;它允许开发者在现有UI元素之上叠加额外的视觉效果或交互功能&#xff0c;而不会影响原有布局系统。本文将详细介绍Adorner的基础概念、核心用法以及实际应用示例。 …...

【React】基于 React+Tailwind 的 EmojiPicker 选择器组件

1.背景 React 写一个 EmojiPicker 组件&#xff0c;基于 emoji-mart 组件二次封装。支持添加自定义背景 、Emoji 图标选择&#xff01;并在页面上展示&#xff01; 2.技术栈 emoji-mart/data 、emoji-mart : emoji 图标库、元数据 tailwindcss: 原子化 CSS 样式库 antd : 组…...

02-Docker 使用

docker:快速构建、运行、管理应用的工具,可以帮助我们下载应用镜像,创建并运行镜像的容器,从而快速部署应用 1、部署mysql 先停掉虚拟机中的MySQL,确保你的虚拟机已经安装Docker,且网络开通的情况下,执行下面命令即可安装MySQL(注意:若服务器上已经有mysql 占用了330…...

html5时钟升级!支持切换深浅模式 Canvas实现现代化动态时钟

HTML5 Canvas实现现代化动态时钟 这里写目录标题 HTML5 Canvas实现现代化动态时钟项目介绍技术实现1. 项目架构2. Canvas绘图实现2.1 表盘绘制2.2 刻度绘制2.3 指针绘制 3. 动画效果4. 主题切换 项目亮点技术要点总结项目收获改进方向结语 项目介绍 本项目使用HTML5 Canvas技术…...