在MFC中,PreTranslateMessage是
虚函数,是用来截获消息的。我们可以通过重载它来处理键盘和鼠标消息。在sdk中,这有所不同,我们必须在回调函数LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)中处理消息。
简介
MFC消息
控制流最具特色的地方是CWnd类的
虚拟函数PreTranslateMessage(),通过
重载这个函数,我们可以改变MFC的消息控制流程,甚至可以作一个全新的控制流出来。只有穿过
消息队列的消息才受PreTranslateMessage()影响,采用SendMessage()或其他类似的方式向窗口直接发送的而不经过消息队列的消息根本不会理睬PreTranslateMessage()的存在。
该函数表示在消息处理(TranslateMessge()和DispatchMessage()等)前所作的操作,如果函数返回值为TRUE,那么消息处理即终止,不会调用TranslateMessge()和DispatchMessage()来翻译和分发消息给相应的窗口;若返回值为FALSE,才会调用翻译和分发消息函数。
在win32程序中,关于消息有两种传递方式:
a.MFC消息,MFC会把所有的消息一条条放到一个AFX_MSGMAP_ENTRY结构中,形成一个数组,该数组存放了所有的消息和与它们相关的参数。也可以说消息是放到消息队列里去了。
b.采用SendMessage()或其他类似的方式向窗口直接发送的而不经过消息队列的消息。这两种方式中只有第一种(穿过消息队列的消息)才受PreTranslateMessage()影响,第二种消息并不会理睬PreTranslateMessage()的存在。
特征
一、是否调用TranslateMessage()和DispatchMessage()是由一个名称为PreTranslateMessage()函数的返回值决定的,如果该函数返回TRUE,则不会把该消息分发给
窗口函数处理。
二、传给PreTranslateMessage()的消息是未经翻译过的消息,它没有经过TranslateMessage()处理。例如:可以在该函数中使用(pMsg->wParam==VK_RETURN)来拦截回车键,wParam中存放的是键盘上字符的虚拟码。
三、在WindowProc里不能处理WM_Char消息。
四、SetWindowText会发送WM_Char给窗口。
五、PeekMessage和GetMessage的区别:
GetMessage在没有消息的时候等待消息,cpu占用率当然低。
PeekMessage没有消息的时候立刻返回,可以在没有消息的时候可以做其他处理,但cpu占用率一般较高。
大多游戏都用PeekMessage();
PeekMessage和GetMessage的区别:
GetMessage在没有消息的时候等待消息,cpu当然低
PeekMessage没有消息的时候立刻返回.
因为游戏不能靠windows消息驱动,所以要用PeekMessage();
附:关于PreTranslateMessage()函数的小程序示例:
BOOLCSearchuserDlg::PreTranslateMessage(MSG*pMsg)
{
if(pMsg->message==WM_KEYDOWN)//判断是否有按键按下
{
switch(pMsg->wParam)
{
caseVK_DOWN://表示是方向键中的向下的键//addhandlecodehere
break;
caseVK_UP://表示是方向键中的向上的键//addhandlecodehere
break;
default:
break;
}
}
}
****************************************************************************************************************************************
PeekMessage一般是用来重载消息循环,避免程序停止响应。举例:
MSGmsg;
if(::PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if(msg.message==WM_QUIT)
{
::PostQuitMessage(-1);
}
if(!AfxGetApp()->PreTranslateMessage(&msg))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
原理
PretranslateMessage的实现,不得不谈到MFC消息循环的实现。MFC通过CWinApp类中的Pumpmessage函数实现消息循环,但是实际的消息循环代码位于CWinThread中,CWinApp只是从CWinThread继承过来。其简化后的代码大概如下:
BOOLCWinThread::PumpMessage()
{
_AFX_THREAD_STATE*pState=AfxGetThreadState();
::GetMessage(&(pState->m_msgCur),NULL,NULL,NULL))
if(!AfxPreTranslateMessage(&(pState->m_msgCur)))
{
::TranslateMessage(&(pState->m_msgCur));
::DispatchMessage(&(pState->m_msgCur));
}
return TRUE;
}
从上可以看到,PumpMessage在实际的TranslateMessage和DispatchMessage发生之前会调用AfxPreTranslateMessage。
而AfxPreTranslateMessage又会调用CWnd::WalkPreTranslateTree(虽然也会调用其他函数,但是这个最为关键),其代码如下:
BOOLPASCALCWnd::WalkPreTranslateTree(HWNDhWndStop,MSG*pMsg)
{
ASSERT(hWndStop==NULL||::IsWindow(hWndStop));
ASSERT(pMsg!=NULL);
//walk from the target window up to the hWnd Stop window checking
//if any window wants to translate this message
for(HWNDhWnd=pMsg->hwnd;hWnd!=NULL;hWnd=::GetParent(hWnd))
{
CWnd*pWnd=CWnd::FromHandlePermanent(hWnd);
if(pWnd!=NULL)
{
//target window is a Cwindow
if(pWnd->PreTranslateMessage(pMsg))
return TRUE;//trappedbytargetwindow(eg:accelerators)
}
//gotto hWnd Stop window without interest
if(hWnd==hWndStop)
break;
}
return FALSE;//no specialprocessing
}
到这里我们可以看到,代码还是很直接的。从接受到消息的窗口层层往上遍历,并调用PretranslateMessage看是否返回TRUE,是则结束,否则继续。
这里有一个地方非常关键:CWnd*pWnd=CWnd::FromHandlePermanent(hWnd)这一句代码从当前AfxModuleThreadState拿到Permanent句柄表,从而找到hWnd对应的CWnd
MFC中PreTranslateMessage是GetMessage(...)函数的下一级操作,即GetMessage(...)从消息队列中获取消息后,交由PreTranslateMessage()处理,若其返回FALSE则再交给TranslateMessage和DispatchMessage处理(进入WindowProc);
如果用SendMessage,则消息直接交到WindowProc处理,所以GetMessage不会取得SendMessage的消息,当然PreTranslateMessage也就不会被调用。 [Page]
如果用PostMessage,则消息进入消息队列,由GetMessage取得,PreTranslateMessage就有机会进行处理。
windows消息处理机制是这样的:
首先系统(也就是windows)把来自硬件(鼠标,键盘等消息)和来自应用程序的消息 放到一个系统消息队列中去.而应用程序需要有自己的消息队列,也就是线程消息队列,每一个线程有自己的消息队列,对于多线程的应用程序就有和线程数目相等的线程消息队列.
windows消息队列把得到的消息发送到线程消息队列,线程消息队列每次取出一条消息发送到指定窗口,不断循环直到程序退出.这个循环就是靠消息环(while(GetMessage()) TranslateMessage();DispatchMessage();实现的.GetMessage()只是从线程消息中取出一条消息,TranslateMessage()把virtuekey消息转化成character消息,如VK_F1会转化成WM_HELP,而DispatchMessage则把取出的消息发送到目的窗口.如果收到WM_CLOSE消息则结束循环,发送postqiutmessage(0),处理WM_DESTROY销毁窗口!
while (GetMessage(&msg, NULL, 0, 0)) //C++ code
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}