MFC中PretranslateMessage的实现

来源:互联网 发布:java里氏替换原则 编辑:程序博客网 时间:2024/05/22 22:52

转载自MFC中PretranslateMessage的实现,该地址不是原始出处。感谢原作者。


    在MFC里面,Pretranslatemessage是一个很重要的虚函数。这个函数的作用这里就不谈了,很多地方都有涉及,这里只谈一下其实现的机制。

 

    谈到PretranslateMessage的实现,便不得不谈到MFC消息循环的实现。MFC通过CWinApp类中的Pumpmessage函数实现消息循环,但是实际的消息循环代码位于CWinThread中,CWinApp只是从CWinThread继承过来。其简化后的代码大概如下:

[cpp] view plain copy
print?
  1. BOOL CWinThread::PumpMessage()  
  2. {  
  3.     _AFX_THREAD_STATE *pState = AfxGetThreadState();  
  4.     ::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL));  
  5.     if (!AfxPreTranslateMessage(&(pState->m_msgCur)))  
  6.     {  
  7.         ::TranslateMessage(&(pState->m_msgCur));  
  8.           
  9.         ::DispatchMessage(&(pState->m_msgCur));  
  10.     }  
  11.     return TRUE;  
  12. }  

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

 

[cpp] view plain copy
print?
  1. BOOL PASCAL CWnd::WalkPreTranslateTree(HWND hWndStop, MSG* pMsg)  
  2. {  
  3.     ASSERT(hWndStop == NULL || ::IsWindow(hWndStop));  
  4.     ASSERT(pMsg != NULL);  
  5.     // walk from the target window up to the hWndStop window checking  
  6.     //  if any window wants to translate this message  
  7.     for (HWND hWnd = pMsg->hwnd; hWnd != NULL; hWnd = ::GetParent(hWnd))  
  8.     {  
  9.         CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);  
  10.         if (pWnd != NULL)  
  11.         {  
  12.             // target window is a C++ window  
  13.             if (pWnd->PreTranslateMessage(pMsg))  
  14.                 return TRUE; // trapped by target window (eg: accelerators)  
  15.         }  
  16.           
  17.         // got to hWndStop window without interest  
  18.         if (hWnd == hWndStop)  
  19.             break;  
  20.     }  
  21.     return FALSE;       // no special processing  
  22. }  


可以看到,代码还是很直接的。从接受到消息的窗口层层往上遍历,并调用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函数(前提是你有要保存这个对象的指针)。

 

每個windows元件(如dialog,button...等)的類別都是繼承自CWnd類別在CWnd類別中 有個member function叫做PreTranslateMessage(),他會傳入一個MSG的指標。指標的內容便是這個message的資訊,包括目標元件,message,wParam,lParam。

我們可以修改其內容,這便會改變要發出去的message或者是只要傳回 TRUE 便可以將這個message吃掉.

 

PreTranslateMessage()的使用方法:

只要在該元件的類別中改寫他的PreTranslateMessage()即可。

範例程式如下

[cpp] view plain copy
print?
  1. BOOL CMyDlg::PreTranslateMessage(MSG *pMsg)  
  2. {  
  3.         BOOL rst = CDialog::PreTranslateMessage(pMsg);  
  4.         if(pMsg->message == WM_KEYDOWN ||  
  5.            pMsg->message == WM_KEYUP    )  
  6.         {  
  7.                 return TRUE;  
  8.         }  
  9.         return rst;  
  10. }  

在以上的程式中會把該CMyDlg中所有鍵盤按下跟放開的message都吃掉,我們便不會再收到任何鍵盤的message了.

要注意的是:不僅僅是dialog會收不到鍵盤message,在這dialog上面的其他window元件也都不會收到.因為任何一個window元件都會受到自己跟他parent 的PreTranslateMessage()影響。我們也可以寫成只影響特定元件 只需要多判斷個hwnd即可。

範例如下:

[cpp] view plain copy
print?
  1. BOOL CMyDlg::PreTranslateMessage(MSG *pMsg)  
  2. {  
  3.         BOOL rst = CDialog::PreTranslateMessage(pMsg);  
  4.   
  5.         if(pMsg->message == WM_KEYDOWN ||  
  6.            pMsg->message == WM_KEYUP    )  
  7.         {  
  8.                 if(pMsg->hwnd == this->m_hWnd)  
  9.                 return TRUE;  
  10.         }  
  11.   
  12.         return rst;  
  13. }  


如此便只會影響到我們所指定的hwnd