PeekMessage与GetMessage PumpMessage()

来源:互联网 发布:atan2函数java 编辑:程序博客网 时间:2024/06/11 01:49

通过GetMessagePeekMessage函数得到消息并推动消息的处理!

其函数原型如下:

//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);  //派送消息 } 

}

所以,PumpMessage是一个“消息泵”,它其实就是调用GetMessage函数从消息队列中取出一个消息并从队列中删除此消息,然后使用TranslateMessage转换消息之后将消息派送出去DispatchMessage。
注意GetMessagePeekMessage有所区别,这里比较适合用GetMessage取消息(当然使用PeekMessage也可以),两者区别详见:《PeekMessage与GetMessage》

如下实例非对话框程序的消息循环机制

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

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

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

总结
PumpMessage起到消息泵的作用,其原理就是使用GetMessage或PeekMessage函数从消息队列中逐个读取消息,然后进行必要处理之后再发送出去。我们可以使用PumpMessage默认处理过程,当然也可以自己修改消息处理的方式。通常我们在我们自己设计窗口时也会自己设计消息循环方式,例如以下代码:

void CProgressWnd::PeekAndPump(BOOL bCancelOnESCkey /*= TRUE*/){ if (m_bModal && ::GetFocus() != m_hWnd) SetFocus(); MSG msg; while (::PeekMessage(&msg, NULL,0,0,PM_NOREMOVE)) { if (bCancelOnESCkey && (msg.message == WM_CHAR) && (msg.wParam == VK_ESCAPE)) OnCancel(); // Cancel button disabled if modal, so we fake it. if (m_bModal && (msg.message == WM_LBUTTONUP)) { CRect rect; m_CancelButton.GetWindowRect(rect); if (rect.PtInRect(msg.pt)) OnCancel(); } if (!AfxGetApp()->PumpMessage()) { ::PostQuitMessage(0); return; } }}

其中是指使用PumpMessage函数自己设计了消息处理过程,我们不需要知道PumpMessage函数内部做了什么处理(当然,经过之前的PumpMessage源码解析你也知道了),只要知道它就是消息处理的核心动力,起到取消息、转换消息并发送消息的作用就行了!而while循环的::PeekMessage(&msg, NULL,0,0,PM_NOREMOVE)只是检测消息队列中是否存在消息罢了(PM_NOREMOVE说明了这一点),如果有消息就调用PumpMessage去消息并处理发送,若果消息队列为空则推出循环。

原创粉丝点击