基于MFC对话框的键盘消息响应

基于MFC对话框的键盘消息响应

PreTranslateMessage作用和使用方法

         PreTranslateMessage 是消息在送给 TranslateMessage 函数之前被调用的,绝大多数本窗口的消息都要通过这里,比较常用,当需要在 MFC 之前处理某些消息时,常常要在这里添加代码 .  


        
MFC消息控制流最具特色的地方是CWnd类的虚拟函数PreTranslateMessage(),通过重载这个函数,可以改变MFC的消息控制流程,甚至可以作一个全新的控制流出来。只有穿过消息队列的消息才受PreTranslateMessage()影响,采用SendMessage()或其他类似的方式向窗口直接发送的而不经过消息队列的消息根本不会理睬PreTranslateMessage()的存在。 


       是否调用TranslateMessage()DispatchMessage()是由一个名称为PreTranslateMessage()函数的返回值决定的,如果该函数返回TRUE,则不会把该消息分发给窗口函数处理。
       传给PreTranslateMessage()的消息是未经翻译过的消息,它没有经过TranslateMessage()处理。可以在该函数中使用(pMsg->wParam==VK_RETURN)来拦截回车键。wParam中存放的是键盘上字符的虚拟码。

PeekMessage
GetMessage的区别:
GetMessage
在没有消息的时候等待消息,cpu当然低
PeekMessage
没有消息的时候立刻返回,所以cpu占用率高。
因为游戏不能靠windows消息驱动,所以要用PeekMessage(); 
     PretranslateMessage
的实现,不得不谈到MFC消息循环的实现。MFC通过CWinApp类中的Pumpmessage函数实现消息循环,但是实际的消息循环代码位于CWinThread中,CWinApp只是从CWinThread继承过来。其简化后的代码大概如下: 

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

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

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

可以看到,代码还是很直接的。从接受到消息的窗口层层往上遍历,并调用PretranslateMessage看是否返回TRUE,是则结束,否则继续。
  这里有一个地方非常关键:CWnd *pWnd = CWnd::FromHandlePermanent(hWnd) 这一句代码从当前AfxModuleThreadState拿到Permanent句柄表,从而找到hWnd对应的CWnd MFCPreTranslateMessageGetMessage(...)函数的下一级操作,即GetMessage(...)从消息队列中获取消息后,交由PreTranslateMessage()处理,若其返回FALSE则再交给TranslateMessageDispatchMessage处理(进入WindowProc);  
如果用SendMessage,   则消息直接交到WindowProc处理,所以GetMessage不会取得SendMessage的消息,当然PreTranslateMessage也就不会被调用。   [Page]
如果用PostMessage,则消息进入消息队列,由GetMessage取得,PreTranslateMessage就有机会进行处理。


windows
消息处理机制是这样的:  
      
首先系统(也就是windows)把来自硬件(鼠标,键盘等消息)和来自应用程序的消息 放到一个系统消息队列中去而应用程序需要有自己的消息队列,也就是线程消息队列,每一个线程有自己的消息队列,对于多线程的应用程序就有和线程数目相等的线程消息队列.  
  windows
消息队列把得到的消息发送到线程消息队列,线程消息队列每次取出一条消息发送到指定窗口,不断循环直到程序退出实现的.这个循环就是靠消息环(while(GetMessage()) TranslateMessage();DispatchMessage();.GetMessage()只是从线程消息中取出一条消息,TranslateMessage()virtue key消息转化成character消息,如VK_F1会转化成WM_HELP,DispatchMessage  则把取出的消息发送到目的窗口.如果收到WM_CLOSE消息则结束循环,发送postqiutmessage(0),处理WM_DESTROY毁窗口!

 while (GetMessage(&msg, NULL, 0, 0))          //C++ code
 {  
        TranslateMessage(&msg);
        DispatchMessage(&msg);
 }

 win32 程序中,关于消息有两种传递方式:

1.      MFC 消息, MFC 会把所有的消息一条条放到一个 AFX_MSGMAP_ENTRY 结构中,形成一个数组,该数组存放了所有的消息和与

它们相关的参数。也可以说是放到消息队列里去。

2.      采用 SendMessage() 或其他类似的方式向窗口直接发送的而不经过消息队列的消息。


这两种方式中只有第一种(穿过消息队列的消息)才受 PreTranslateMessage() 影响,

第二种消息并不会理睬 PreTranslateMessage() 的存在。

1)      是否调用 TranslateMessage() 和 DispatchMessage() 是由一个名称为 PreTranslateMessage() 函数的返回值决定的,如果该函数返回

 TRUE ,则不会把该消息分发给窗口函数处理。

2)      传给 PreTranslateMessage() 的消息是未经翻译过的消息,它没有经过 TranslateMessage() 处理。可以在该函数中使用 

(pMsg->wParam==VK_RETURN) 来拦截回车键。

3)      在 WindowProc 里不能处理 WM_Char 消息。( WindowProc 函数见 MFC 消息响应机制一文)

4)      SetWindowText 会发送 WM_Char 给窗口。

5)      PeekMessage 和 GetMessage 的区别:

6)      GetMessage 在没有消息的时候等待消息, cpu 当然低

7)      PeekMessage 没有消息的时候立刻返回,所以 cpu 占用率高。因为游戏不能靠 windows 消息驱动,所以要用 PeekMessage();


另一篇文章中:

            在一个 WIN32 程序中, WINDOWS 会将消息传递给相应的窗口。但是消息不是立即就被传递给相应的窗口,而是会从整个程序最顶层

的窗口传递到下一级窗 口,再传递到下一级窗口,直到传递给目标窗口。在整个过程中,有些消息,在某些特定的情况下,无法默认传递到目

标窗口的。比如用户在 EDIT 控件中按下回 车键, CANCEL 键等,如果 EDIT 窗口之前有对话框窗口,对话框会默认处理回车消息(即响应 

ONOK 函数,然后关闭对话框),然后退出消息传递。所以 EDIT 会收不到。要解决这个问题,可以在 EDIT 窗口之前所有的对话框中重载 

PreTranslateMessage 函数,然后在函数内加上:

if (pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_RETURN)     // 如果消息类型为WM_KEYDOWN 并且用户按下的是回车 
  return FALSE;    // 不翻译消息,直接将消息传递下去。具体可查 MSDN 。注意,这里返回值不能为TRUE , TRUE 的意思是翻译消息后退出消

息传递,如此一来虽然也能避开对话框默认处理,但是会退出消息传递,这样 EDIT 控件照样得不到消息。

如此,就可避开对话框默认处理,将消息传递下去。注意:只有对话框才会默认处理按下回车,CANCEL 消息,其他控件窗口则不会,所以在其

他窗口中不必重载 PreTranslateMessage 函数,当然如果重载了也不会错。


附:关于 PreTranslateMessage() 函数的小程序示例:

  1  BOOL CSearchuserDlg::PreTranslateMessage(MSG *  pMsg) 
  3  {
  5        if  (pMsg -> message == WM_KEYDOWN)   //  判断是否有按键按下 
  7       { 
  9              switch  (pMsg -> wParam) 
 11             { 
 13              case  VK_DOWN:      //  表示是方向键中的向下的键
 15                   // add handle code here
 17                   break  ;
 19              case  VK_UP:       //  表示是方向键中的向上的键
 21                   // add handle code here
 23                   break  ;
 25              default  :
 27                   break  ;
 29             }
 31       }
 33  }  
 36 
 37  BOOL CMyDlg::PreTranslateMessage(MSG *  pMsg)
 38  {
 39        //  TODO: Add your specialized code here and/or call the base class 
 41        //  按键相应
 42        if  (pMsg -> message  ==  WM_KEYDOWN)
 43       {
 44              if  (pMsg -> wParam  ==  VK_DOWN)
 45             {
 46                   //  向下键按下
 47             }
 48              else   if  (pMsg -> wParam  ==  VK_RIGHT)
 49             {
 50                   //  向右键按下
 51             }
 52              else   if  (pMsg -> wParam  ==  VK_LEFT)
 53            {
 54                   //  向左键按下
 55             }
 56              else   if  (pMsg -> wParam  ==  VK_UP)
 57             {
 58                   //  向上键按下
 59             }
 60              else   if  (pMsg -> wParam  ==  VK_SHIFT)
 61             {
 62                   // VK_LSHIFT 为左 Shift 键按下
 63                   // Shift 键按下
 64             }
 65              else   if  (pMsg -> wParam  ==  VK_CONTROL)
 66             {
 67                   // Ctrl 键按下
 68             }
 69              else   if  (pMsg -> wParam >= VK_NUMPAD0  &&  pMsg -> wParam <= VK_NUMPAD9)
 70             {
 71                   //  小键盘数字键按下
 72             }
 73              else   if  (pMsg -> wParam >= 0x30   &&  pMsg -> wParam <= 0x39 )
 74             {
 75                   //  数字键按下 ( 我记得不能使用 VK_0)
 76             }
 77              else   if  (pMsg -> wParam >= 0x41   &&  pMsg -> wParam <= 0x5A )
 78             {
 79                   //  键盘字母键按下 ( 我记得不能使用 VK_A)
 80             }
 81              else   if  (pMsg -> wParam  ==  VK_BACK)
 82             {
 83                   //  退格键按下
 84             }
 85              else   if  (pMsg -> wParam  ==  VK_DELETE)
 86             {
 87                   //  删除键按下
 88             }
 89              else   if  (pMsg -> wParam  ==  VK_F1)
 90             {
 91                   // F1 键按下
 92             }
 93 
 94              // return true;   //  使消息不再进行处理
 95       }
 96 
 97        if  (pMsg -> message  ==  WM_KEYUP)
 98       {
 99              if  (pMsg -> wParam  ==  VK_SHIFT)
100             {
101                   // Shift 键弹起
102             }
103              else   if  (pMsg -> wParam  ==  VK_CONTROL)
104             {
105                   // Ctrl 键弹起
106             }
107              // return true;   //  使消息不再进行处理
108       }
109 
110        return  CDialog::PreTranslateMessage(pMsg);
111  }
112 
113  //  同时按下 ctrl 键
114  BOOL   CDemo_DevStudioView::PreTranslateMessage(MSG *    pMsg)    //  根据键盘上的按键对图形进行相应的操作  
116 
117        if  (pMsg -> message == 256 )    //    256 有键按下, 46   DEL 键  
118       { 
119              switch  (pMsg -> wParam)   
120             { 
121                   ///  向左键被按下    
122              case      37
123                  { 
124                        //  同时按下了 CTRL 键  
125                        if  (::GetKeyState(VK_CONTROL)        <         0 )       
126                       {       
127                       } 
128                  } 
129             } 
130       }
131  }

本文转自: http://blog.csdn.net/shengliz/article/details/4979286
                     http://blog.csdn.net/zhanghui_hn/article/details/7456913

你可能感兴趣的:(基于MFC对话框的键盘消息响应)