【消息循环&消息泵】MFC消息循环和消息泵

  首先,应该清楚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 !=

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值