前言
此前系列文章回顾:
在MFC中使用Qt(一):玩腻了MFC,试试在MFC中使用Qt!(手动配置编译Qt)
在MFC中使用Qt(三):通过编辑项目文件(.vcxproj)实现Qt的自动化编译流程
在MFC中使用Qt(四):使用属性表(Property Sheet)实现自动化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 away
SetFocus(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_WITHMFC
else 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;
}
#endif
recursion = false;
#if QT_VERSION < 0x050000
return QApplication::winEventFilter(msg, result);
#else
Q_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 未同时运行两个独立事件循环,而是通过以下方式实现协作而非并行:
- Qt 主循环驱动:通过
PeekMessage
处理所有消息(包括 MFC 窗口消息)。 - MFC 逻辑嵌入:消息过滤器和钩子确保 MFC 的
PreTranslateMessage
、OnIdle
等关键逻辑被触发。 - 无阻塞设计:Qt 的
exec()
内部以非阻塞方式轮询消息,避免独占线程。
这种设计使得 MFC 窗口能够响应消息,而 Qt 控件也能正常更新,实现无缝混合运行。