通过GetMessage或PeekMessage函数得到消息并推动消息的处理!
其函数原型如下:
//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); //派送消息 }
}
如下实例:非对话框程序的消息循环机制
//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 On
Idle while in bIdle state if (!On Idle(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代表程序是否空闲。他的意思就是,如果程序是空闲并且消息队列中没有要处理的消息,那么调用虚函数On
NOTE:MS用临时对象是出于效率上的考虑,使内存有效利用,并能够在空闲时自动撤销资源。关于由句柄转换成对象,可以有若干种方法。一般是先申明一个对象obj,然后使用obj.Attatch来和一个句柄绑定。这样产生的对象是永久的,你必须用obj.Detach来释放对象。
2 ,第二个内循环 phase2 。在这个循环内先启动 消息泵(PumpMessage) , 如果不是 WM_QUIT 消息, 消息泵将消息发送出去(::DispatchMessage) 。消息的目的地是消息结构中的 hwnd 字段所对应的窗口。其中是指使用PumpMessage函数自己设计了消息处理过程,我们不需要知道PumpMessage函数内部做了什么处理(当然,经过之前的PumpMessage源码解析你也知道了),只要知道它就是消息处理的核心动力,起到取消息、转换消息并发送消息的作用就行了!而while循环的 :: PeekMessage (&msg, NULL,0,0, PM_NOREMOVE )只是检测消息队列中是否存在消息罢了( PM_NOREMOVE 说明了这一点),如果有消息就调用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; } } }