MFC中PretranslateMessage的实现

(不小心删掉了这篇文章,现在把它翻出来,重新发表)

在MFC里面,PretranslateMessage是一个很重要的虚函数。这个函数的作用这里就不谈了,很多地方都有涉及,这里只谈一下其实现的机制。
谈到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));
}

returnTRUE;
}

可以看到,PumpMessage在实际的TranslateMessage和DispatchMessage发生之前会调用AfxPreTranslateMessage,AfxPreTranslateMessage又会调用CWnd::WalkPreTranslateTree(虽然也会调用其他函数,但是这个最为关键),其代码如下:

BOOLPASCALCWnd::WalkPreTranslateTree(HWNDhWndStop,MSG * pMsg)
... {
ASSERT(hWndStop
==NULL||::IsWindow(hWndStop));
ASSERT(pMsg
!=NULL);

//walkfromthetargetwindowuptothehWndStopwindowchecking
//ifanywindowwantstotranslatethismessage

for(HWNDhWnd=pMsg->hwnd;hWnd!=NULL;hWnd=::GetParent(hWnd))
...{
CWnd
*pWnd=CWnd::FromHandlePermanent(hWnd);
if(pWnd!=NULL)
...{
//targetwindowisaC++window
if(pWnd->PreTranslateMessage(pMsg))
returnTRUE;//trappedbytargetwindow(eg:accelerators)
}


//gottohWndStopwindowwithoutinterest
if(hWnd==hWndStop)
break;
}

returnFALSE;//nospecialprocessing
}

可以看到,代码还是很直接的。从接受到消息的窗口层层往上遍历,并调用PretranslateMessage看是否返回TRUE,是则结束,否则继续。
这里有一个地方非常关键:CWnd *pWnd = CWnd::FromHandlePermanent(hWnd) 这一句代码从当前AfxModuleThreadState拿到Permanent句柄表,从而找到hWnd对应的CWnd对象。关于PreTranslateMessage有一个常见的问题就是与此有关:如果编写了一个MFC DLL并从另外的一个MFC主工程之中调用这个MFC DLL中的Modeless Dialog的话,Modeless Dialog的PreTranslateMessage不会被调。因为MFC DLL和这个MFC工程拥有不同的AfxModuleThreadState,因此在MFC DLL中创建的modeless CDialog对象不在MFC工程的句柄表中(CWnd::FromhandlePermanent返回NULL),因此虽然MFC主工程中的CWinApp的Pretranslatemessage会被调(注意此时Dialog的消息循环在MFC主工程里面),但是不会调用MFC DLL中创建的那个modeless CDialog的PreTranslateMessage函数。因此需要特殊处理。一般有两种方法:
一种是直接在MFC主工程中的CWinApp::PreTranslatemessage里面调用MFC DLL的CWinApp::PreTranslateMessage(可以专门在MFC DLL中export一个专门的函数来做这件事情)。另外的方法是使用钩子,在钩子消息处理函数之中,判断目标窗口是否是当前具有焦点的窗口,如果是,则直接调用目标窗口的PreTranslateMessage函数(前提是你有要保存这个对象的指针)。

你可能感兴趣的:(thread,mfc)