深入探讨MFC消息循环和消息泵(一)

深入探讨MFC消息循环和消息泵 深入探讨MFC消息循环和消息泵,比较深入详细的解说Windows消息循环机制 立即下载

首先,应该清楚MFC的消息循环(::GetMessage,::PeekMessage),消息泵(CWinThread::PumpMessage)和MFC的消息在窗口之间的路由是两件不同的事情。在MFC的应用程序中(应用程序类基于CWinThread继承),必须要有一个消息循环,他的作用是从应用程序的消息队列中读取消息,并把它派送出去(::DispatchMessage)。而消息路由是指消息派送出去之后,系统(USER32.DLL)把消息投递到哪个窗口,以及以后消息在窗口之间的传递是怎样的。

  消息分为队列消息(进入线程的消息队列)和非队列消息(不进入线程的消息队列)。对于队列消息,最常见的是鼠标和键盘触发的消息,例如WM_MOUSERMOVE,WM_CHAR等消息;还有例如:WM_PAINT、WM_TIMER和WM_QUIT。当鼠标、键盘事件被触发后,相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息,然后输送到系统消息队列,由Windows系统负责把消息加入到相应线程的消息队列中,于是就有了消息循环(从消息队列中读取并派送消息)。还有一种是非队列消息,他绕过系统队列和消息队列,直接将消息发送到窗口过程。例如,当用户激活一个窗口系统发送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。创建窗口时发送WM_CREATE消息。在后面你将看到,MS这么设计是很有道理的,以及他的整套实现机制。

  这里讲述MFC的消息循环,消息泵。先看看程序启动时,怎么进入消息循环的:



_tWinMain ->AfxWinMain ->AfxWinInit ->CWinThread::InitApplication ->CWinThread::InitInstance ->CWinThread::Run




  非对话框程序的消息循环的事情都从这CWinThread的一Run开始...

  第一部分:非对话框程序的消息循环机制。



//thrdcore.cpp
// main running routine until thread exits
int CWinThread::Run()
{
ASSERT_VALID(this);

// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;

// acquire and dispatch messages until a WM_QUIT message is received.
for (;;)
{
  // phase1: check to see if we can do idle work
  while (bIdle &&
   !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
  {
   // call OnIdle while in bIdle state
   if (!OnIdle(lIdleCount++))
    bIdle = FALSE; // assume "no idle" state
  }

  // phase2: pump messages while available
  do
  {
   // pump message, but quit on WM_QUIT
   if (!PumpMessage())
    return ExitInstance();

   // reset "no idle" state after pumping "normal" message
   if (IsIdleMessage(&m_msgCur))
   {
    bIdle = TRUE;
    lIdleCount = 0;
   }

  } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
}    //无限循环,退出条件是收到WM_QUIT消息。

ASSERT(FALSE);  // not reachable
}

这是一个无限循环,他的退出条件是收到WM_QUIT消息:



if (!PumpMessage())
    return ExitInstance();

   在PumpMessage中,如果收到WM_QUIT消息,那么返回FALSE,所以ExitInstance()函数执行,跳出循环,返回程序的退出代码。所以,一个程序要退出,只用在代码中调用函数

  VOID PostQuitMessage( int nExitCode )。指定退出代码nExitCode就可以退出程序。

  下面讨论一下这个函数Run的流程,分两步:

  1,第一个内循环phase1。bIdle代表程序是否空闲。他的意思就是,如果程序是空闲并且消息队列中没有要处理的消息,那么调用虚函数OnIdle进行空闲处理。在这个处理中将更新UI界面(比如工具栏按钮的enable和disable状态),删除临时对象(比如用FromHandle得到的对象指针。由于这个原因,在函数之间传递由FromHandle得到的对象指针是不安全的,因为他没有持久性)。OnIdle是可以重载的,你可以重载他并返回TRUE使消息循环继续处于空闲状态。

  NOTE:MS用临时对象是出于效率上的考虑,使内存有效利用,并能够在空闲时自动撤销资源。关于由句柄转换成对象,可以有若干种方法。一般是先申明一个对象obj,然后使用obj.Attatch来和一个句柄绑定。这样产生的对象是永久的,你必须用obj.Detach来释放对象。

  2,第二个内循环phase2。在这个循环内先启动消息泵(PumpMessage),如果不是WM_QUIT消息,消息泵将消息发送出去(::DispatchMessage)。消息的目的地是消息结构中的hwnd字段所对应的窗口。



//thrdcore.cpp
BOOL CWinThread::PumpMessage()
{
ASSERT_VALID(this);

//如果是WM_QUIT就退出函数(return FALSE),这将导致程序结束.
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)) {
#ifdef _DEBUG
  if (afxTraceFlags & traceAppMsg)
   TRACE0("CWinThread::PumpMessage - Received WM_QUIT./n");
  m_nDisablePumpCount++; // application must die
   // Note: prevents calling message loop things in 'ExitInstance'
   // will never be decremented
#endif
  return FALSE;
}

#ifdef _DEBUG
if (m_nDisablePumpCount != 0)
{
  TRACE0("Error: CWinThread::PumpMessage called when not permitted./n");
  ASSERT(FALSE);
}
#endif

#ifdef _DEBUG
if (afxTraceFlags & traceAppMsg)
  _AfxTraceMsg(_T("PumpMessage"), &m_msgCur);
#endif

// process this message

if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))





{
  ::TranslateMessage(&m_msgCur); //键转换
  ::DispatchMessage(&m_msgCur); //派送消息
}
return TRUE;
}

?
  在这一步有一个特别重要的函数大家一定认识:PreTranslateMessage。这个函数在::DispatchMessage发送消息到窗口之前,进行对消息的预处理。PreTranslateMessage函数是CWinThread的成员函数,大家重载的时候都是在View类或者主窗口类中,那么,它是怎么进入别的类的呢?代码如下:



//thrdcore.cpp
BOOL CWinThread::PreTranslateMessage(MSG* pMsg)
{
ASSERT_VALID(this);

// 如果是线程消息,那么将会调用线程消息的处理函数
if (pMsg->hwnd == NULL && DispatchThreadMessageEx(pMsg))
  return TRUE;

// walk from target to main window
CWnd* pMainWnd = AfxGetMainWnd();
if (CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg))
  return TRUE;

// in case of modeless dialogs, last chance route through main
//   window's accelerator table
if (pMainWnd != NULL)
{
   CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd);
   if (pWnd->GetTopLevelParent() != pMainWnd)
   return pMainWnd->PreTranslateMessage(pMsg);
}

return FALSE;   // no special processing
}


  由上面这个函数可以看出:

  第一,如果(pMsg->hwnd == NULL),说明这是一个线程消息。调用CWinThread::DispatchThreadMessageEx到消息映射表找到消息入口,然后调用消息处理函数。

  NOTE: 一般用PostThreadMessage函数发送线程之间的消息,他和窗口消息不同,需要指定线程id,消息激被系统放入到目标线程的消息队列中;用ON_THREAD_MESSAGE( message, memberFxn )宏可以映射线程消息和他的处理函数。这个宏必须在应用程序类(从CWinThread继承)中,因为只有应用程序类才处理线程消息。如果你在别的类(比如视图类)中用这个宏,线程消息的消息处理函数将得不到线程消息。

  第二,消息的目标窗口的PreTranslateMessage函数首先得到消息处理权,如果函数返回FALSE,那么他的父窗口将得到消息的处理权,直到主窗口;如果函数返回TRUE(表示消息已经被处理了),那么就不需要调用父类的PreTranslateMessage函数。这样,保证了消息的目标窗口以及他的父窗口都可以有机会调用PreTranslateMessage--在消息发送到窗口之前进行预处理(如果自己处理完然后返回FALSE的话 -_-b),如果你想要消息不传递给父类进行处理的话,返回TRUE就行了。

  第三,如果消息的目标窗口和主窗口没有父子关系,那么再调用主窗口的PreTranslateMessage函数。为什么这样?由第二步知道,一个窗口的父窗口不是主窗口的话,尽管它的PreTranslateMessage返回FALSE,主窗口也没有机会调用PreTranslateMessage函数。我们知道,加速键的转换一般在框架窗口的PreTranslateMessage函数中。

  我找遍了MFC中关于加速键转换的处理,只有CFrameWnd,CMDIFrameWnd,CMDIChildWnd等窗口类有。所以,第三步的意思是,如果消息的目标窗口(他的父窗口不是主窗口,比如一个这样的非模式对话框)使消息的预处理继续漫游的话(他的PreTranslateMessage返回FALSE),那么给一次机会给主窗口调用PreTranslateMessage(万一他是某个加速键消息呢?),这样能够保证在有非模式对话框的情况下还能保证主窗口的加速键好使。

消息循环与消息泵(mfc消息循环机制详解) 消息循环与消息泵(mfc消息循环机制详解),详细介绍mfc消息循环机理,个人觉得的很经典(有本人评点及注释),非常值得一下! 立即下载

相关推荐

别再手动写用例了!用AI Agent自动生成测试用例的保姆级实战(附Python代码)

本文详细介绍了如何利用AI Agent技术自动生成测试用例,大幅提升测试工程师的工作效率。通过Python代码实战演示,从环境搭建到核心Agent构建(需求解析、场景挖掘、用例编排),再到与企业工具链(TestRail、Jira)的深度集成,帮助团队实现测试用例的智能化生成与管理。

weixin_30897233的博客 646

消息泵

希望一下对消息泵的理解能个您带来帮助! 消息泵也就是消息(处理)循环 (Message Loop),每个基于事件驱动编写出来的 Windows 程序都应该有一个。 消息循环(Message Loop)是程序的心脏,保证程序的正常运行,它的形状大概如下面的结构。 while (true) { // 内部处理 } 可见,它应该是不断循环的一段代码, 打破它的循环可以有条件的使用

icatchyou的专栏 2262

股票行情接口,最全的接入方法

股票行情接口一般有两种,一种是WebSocket,一种是http,通过WebSocket可以获取逐笔的股票tick数据,实时并且高效;通过http接口可以获取股票行情接口中的股票历史数据;WebSocket是一种高效的双向通信协议,它允许数据的实时推送,避免了不断的轮询请求。其次,WebSocket允许您订阅特定的数据源或股票数据产品,只接收您感兴趣的信息,提高了数据的效率和可用性。通过接入股票行情数据的HTTP接口,您可以将数据导入到自己的分析工具或系统中,进行更深入的数据分析、模型构建和决策支持。

apifinance的博客 3425

C#消息泵探索(一)

消息泵,又叫消息循环。 消息循环使用一个图形化用户界面下的Microsoft Windows。具有GUI 的Windows 程序是事件驱动的。Windows为每个创建窗口的线程维护一个单独的消息队列。通常只有第一个线程创建窗口。Windows 放置消息每当鼠标活动发生在该线程的窗口上时,每当该窗口具有焦点时键盘活动发生时,以及其他时候,都将进入该队列。进程还可以将消息添加到自己的队列中。为了接受用户输入以及出于其他原因,具有窗口的每个线程必须不断地从其队列中检索消息,并对其采取行动。通过编写一个循环调用

杞人忧天的博客 1319

英飞凌功率MOSFET SPICE 瞬态仿真不收敛问题解决(基于TINA)

英飞凌功率MOSFET SPICE仿真容易出现不收敛问题,这个对于IGBT和SIC的管子应该是一样的,在瞬态仿真分析中,出现如下图所示的报错,Convergence problem。spice是为线性电路设计的,例如运算放大器、线性电源等,不是专门仿真开关电路的。在瞬态仿真时,仿真器是要不断迭代求解的,开关快速的跳变,容易在迭代式出现不收敛的问题。,由于时间步长减少,会消耗更多的计算机内存,如果运行多次内存消耗过大,会报“Out of memory”.解决方法是:报错工程,重启软件,内存重新计算。

miaoxiaokeji的博客 2297

手把手教你用Multisim搭建FM直接调频电路(附完整波形分析)

本文通过Multisim仿真软件,手把手教你搭建FM直接调频电路。详细介绍了从核心概念理解、压控振荡器(VCO)参数设置、电路搭建与波形观测,到带通滤波器设计与信号优化的完整流程。通过对比滤波前后的时域波形与频谱分析,帮助读者直观理解频率调制原理,并提供了常见问题的排查思路与参数调试方法,将抽象的通信理论转化为可操作的实践技能。

weixin_29230773的博客 214

MFC中消息循环机制

在MFC中通过重载CWnd类中的虚拟函数PreTranslateMessage,可以改变消息控制流程。 只有经过消息队列的消息才受PreTranslateMessage的影响,使用SendMessage函数发送或其他类似方式直接向窗口发送消息是不经过消息队列的。 在消息处理函数中,不能处理WM_CHAR消息。 SetWindowText会发送WM_CHAR字符消息。 GetMessage在

8436

开源机器人库orocos KDL 学习笔记(二):Geometric

开源机器人库orocos KDL 学习笔记二:Geometric 如果按照上一篇搭建的VS2015 orocos KDL环境,会出现3个例程:geometry, chainiksolverpos_lma_demo, trajectory_example. 这一篇看一下geometry的例程。 在frames.hpp中,定义了这些class: - class Vector - class R

行者无疆的博客 6703

Android SurfaceFlinger——概述(一)

对于系统渲染流程的理解是指导 SurfaceFlinger 设计的核心思想,从 Android 4.1 一直到 Android 9.0 都没有太大的变化。只要抓住这五个核心思想,我们阅读 SurfaceFlinger 的难度就会下降不少。下面看一下 SurfaceFlinger 的体系和 Skia 以及 View的绘制流程的关系。framework 面向开发者所有的 View 是便于开发的控件,里面仅仅只是提供了当前 View 各种属性以及功能。

小旭的专栏 2584

30分钟上手YOLO26 nano:INT8量化部署到智能相机,推理速度提升43%

微调代码(仅需5行,数据集遵循COCO格式)data="custom_data.yaml", # 数据集配置文件epochs=20, # 少量轮数即可收敛batch=8,imgsz=640model.save("yolov26n_custom.pt") # 保存微调后的模型本文在30分钟内完成了YOLO26 nano的INT8量化与智能相机部署,核心步骤为环境准备→模型获取→ONNX导出→INT8量化→部署推理。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 987

Windows消息机制

Windows消息机制

zhangchuan7758的博客 1462

华为OD机试2025A卷 - BOSS的收入(Java & Python& JS & C++ & C )

华为OD机试2025A卷 - BOSS的收入(Jav一个XX产品行销总公司,只有一个boss,其有若干一级分销,一级分销又有若干二级分销,每个分销只有唯一的上级分销。规定,每个月,下级分销需要将自己的总收入(自己的+下级上交的)每满100元上交15元给自己的上级。现给出一组分销的关系,和每个分销的收入,请找出boss并计算出这个boss的收入。输入:分销关系和收入:[[分销id 上级分销id 收入], [分销id 上级分销id 收入], [分销id 上级分销id 收入]]输出:[boss的ID, 总收入]

算法大师! 1172

[转]深入探讨MFC消息循环和消息泵

导读:   首先,应该清楚MFC的消息循环(::GetMessage,::PeekMessage),消息泵(CWinThread::PumpMessage)和MFC的消息在窗口之间的路由是两件不同的事情。在MFC的应用程序中(应用程序类基于CWinThread继承),必须要有一个消息循环,他的作用是从应用程序的消息队列中读取消息,并把它派送出去(::DispatchMessage)。而消息

557

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

上一篇: 天人手发``
下一篇: Window 消息大全使用详解
普通网友
博客等级 码龄22年 1万+粉丝 3156原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值