Windows消息机制-PreTranslateMessage

PreTranslateMessage作用和使用方法

Windows消息机制的流程:

  A. 操作系统接收应用程序的窗口消息,将消息投递到该应用程序的消息队列中

  B. 应用程序在消息循环中调用GetMessage函数从消息队列中取出一条一条的消息,取出消息后,应用程序可以对消息进行一些预处理。

  C. 应用程序调用DispatchMessage,将消息回传给操作系统。

  D. 系统利用WNDCLASS结构体的lpfnWndProc成员保存的窗口过程函数的指针调用窗口过程,对消息进行处理。

  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继承过来。其简化后的代码大概如下: 
  BOOL CWinThread::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(虽然也会调用其他函数,但是这个最为关键),其代码如下: 
  BOOL PASCAL CWnd::WalkPreTranslateTree(HWND hWndStop, MSG* pMsg) 
  { 
  ASSERT(hWndStop == NULL || ::IsWindow(hWndStop)); 
  ASSERT(pMsg != NULL); 
   
  // walk from the target window up to the hWndStop window checking 
  // if any window wants to translate this message 
   
  for (HWND hWnd = pMsg->hwnd; hWnd != NULL; hWnd = ::GetParent(hWnd)) 
  { 
  CWnd* pWnd = CWnd::FromHandlePermanent(hWnd); 
  if (pWnd != NULL) 
  { 
  // target window is a C window 
  if (pWnd->PreTranslateMessage(pMsg)) 
  return TRUE; // trapped by target window (eg: accelerators) 
  } 
   
  // got to hWndStop window without interest 
  if (hWnd == hWndStop) 
  break; 
  } 
  return FALSE; // no special processing 
  } 
   
  可以看到,代码还是很直接的。从接受到消息的窗口层层往上遍历,并调用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()把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);
 }

/*************************二*******************/

函数原型:virtual BOOL PreTranslateMessage( MSG *pMsg) 

1、在MFC中,PreTranslateMessage是虚函数,是用来截获消息的。我们可以通过重载它来处理键盘和鼠标消息。在sdk中,这有所不同,我们必须在回调函
数 LRESULT   CALLBACK   WndProc(HWND   hWnd,   UINT   message,   WPARAM   wParam,   LPARAM   lParam)中处理消息。它和PreTranslateMessage起的作用是类似的,只是MFC封装的更好而已。
2、重载该函数可以实现窗口消息在派发给窗口函数TranslateMessage()和DispatchMessae()之前的过滤,缺省的实现是完成加速键的翻译。 

3、 该函数表示在消息处理(TranslateMessge()和DispatchMessage()等)前所作的操作,如果函数返回值为TRUE,那么消息 处理即终止,不会调用TranslateMessge()和DispatchMessage()来翻译和分发消息给相应的窗口;若返回值为FALSE,才 会调用翻译和分发消息函数。

4、在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消息。(WindowProc函数见MFC消息响应机制一文)
四、SetWindowText会发送WM_Char给窗口。

、PeekMessage和GetMessage的区别:
    GetMessage在没有消息的时候等待消息,cpu当然低
    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()函数的小程序示例:
BOOL CSearchuserDlg::PreTranslateMessage(MSG* pMsg) 

{
           if(pMsg->message==WM_KEYDOWN)  //判断是否有按键按下     

           {
                  switch(pMsg->wParam)        

           {
                     case VK_DOWN:     //表示是方向键中的向下的键               //add handle code here           

                        break;
                    case VK_UP:      //表示是方向键中的向上的键                //add handle code here           

                        break;           

                 default:           

                      break;        

           }    

       } 

}
**********************************************************************
PeekMessage 一般是用来重载消息循环,避免程序停止响应。           举例:        
  MSG   msg;   
  if(::PeekMessage(&msg,NULL,0,0,PM_REMOVE))

{          

        if(msg.message==WM_QUIT)          

          {   
            ::PostQuitMessage(-1);            

         }   

        if(!AfxGetApp()->PreTranslateMessage(&msg))            

       {   
            ::TranslateMessage(&msg);                

            ::DispatchMessage(&msg);            

        }      

}
******************************************************************************
界面上有很多个控件,如edit,如何知道当前的焦点是在哪个控件上面?
1. GetFocus()可以得到当前控件的句柄, 你再枚举子窗口句柄判断, 跟得到的句柄一致的即为哪个被点中。
2. CWnd* pCtrl = CWnd::GetFocus();  

int iCtrlID = pCtrl->GetDlgCtrlID();  

if(iCtrlID == IDC_EDIT1)  {  } 
else if(iCtrlID == IDC_EDIT2)  {  }  ... 
3. GetFocus() 功能:确定当前焦点位于哪个控件上。函数执行成功时返回当前得到焦点控件的窗口句柄,发生错误时返回无效引用。
用法: 应用程序利用IsValid()函数可以检测GetFocus()是否返回有效的控件引用。同时,使用TypeOf()函数可以确定控件的类型。
 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值