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在实际的TranslateMessage和DispatchMessage发生之前会调用AfxPreTranslateMessage,AfxPreTranslateMessage又会调用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 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);
}
在 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 }
posted on 2012-09-05 12:48
王海光 阅读(1679)
评论(0) 编辑 收藏 引用 所属分类:
MFC