WM_CHAR消息分析 |
责任编辑:admin 更新日期:2005-8-6 |
下载本文所附源代码 应用程序中键盘消息从WM_KEYFIRST到WM_KEYLAST那么多,但我们知道最原始的键盘消息只有两个那就是WM_KEYDOWN,WM_KEYUP,在键盘钩子中我们能截获的也就这两个消息,那其它消息是从何产生的,如何动作的呢?下面我们就WM_CHAR消息来分析一下。 一个键按下后,便会有一个或多个WM_KEYDOWN消息产生,这些消息会从系统消息队列发送到目标窗口的线程的消息队列中,这样在对应线程处理消息时便会对此消息处理,并在处理的过程中产生一系列的其它键盘消息,其中便有WM_CHAR。 以下是向对话框中的编辑框输入'a'的部分处理过程(以所附源码调试、整理而得) CGetInputDlg::DoModal() //对话框创建 往编辑框按下一键'a' 001 CGetInputDlg::RunModalLoop //消息循环,在这里peek到WM_KEYDOWN 002 CGetInputDlg::PumpMessage //消息泵,取消息、翻译、处理这些消息 003 CGetInputDlg::PreTranslateMessage //线程开始消息预处理 004 CGetInputDlg::WalkPreTranslateTree //从目标窗口到主窗口历遍 005 CMyEdit::PreTranslateMessage //目标窗口,没有处理(返回FALSE)。 006 CGetInputDlg::PreTranslateMessage //子窗口没有处理,所以流到父窗口 007 CDialogDlg::PreTranslateMessage //由基类处理 ... 008 CMyEdit::WindowProc 009 CMyEdit::OnWndMsg 010 CMyEdit::OnKeyDown 011 CMyEdit::DefWindowProc 012 CGetInputDlg::RunModalLoop //在这里peek到WM_CHAR,不过一般情况下之间会有几个WM_KICKIDLE ... 由上有人就会说,这并不能说明WM_CHAR是WM_KEYDOWN消息处理过程中产生的,这仅仅说明WM_CHAR在WM_KEYDOWN之后。i嗯,大家再看看下面。 这些是所附源码中的调试输出信息,无~~代表对应函数的进入,有~~代表一个函数的返回点,msg后的值为消息的16进制值(WM_KEYDOWN=0X100, WM_KEYUP=0X101, WM_CHAR=0X102),haveCharMsg为消息队列中是否有WM_CHAR消息(通过peekmessage来实现) 英文输入'a',对应调试信息整理如下: 对应005 101 CMyEdit::PreTranslateMessage msg=100, haveCharMsg=0 102 ~~CMyEdit::PreTranslateMessage msg=100, haveCharMsg=0, Ret=0 对应006 103 CGetInputDlg::PreTranslateMessage msg=100, haveCharMsg=0 105 CMyEdit::OnKeyDown haveCharMsg=1 106 CMyEdit::DefWindowProc msg=100, haveCharMsg=1 107 ~~CMyEdit::DefWindowProc msg=100, haveCharMsg=1, Ret=1 108 ~~CMyEdit::OnKeyDown haveCharMsg=1 109 ~~CGetInputDlg::PreTranslateMessage msg=100, haveCharMsg=1, Ret=1 110 CMyEdit::PreTranslateMessage msg=102, haveCharMsg=0 111 ~~CMyEdit::PreTranslateMessage msg=102, haveCharMsg=0, Ret=0 112 CGetInputDlg::PreTranslateMessage msg=102, haveCharMsg=0 113 CMyEdit::OnChar 61 114 CMyEdit::DefWindowProc msg=102, haveCharMsg=0 115 ~~CMyEdit::DefWindowProc msg=102, haveCharMsg=0, Ret=1 116 ~~CMyEdit::OnChar 61 117 ~~CGetInputDlg::PreTranslateMessage msg=102, haveCharMsg=0, Ret=1 118 CMyEdit::PreTranslateMessage msg=101, haveCharMsg=0 119 ~~CMyEdit::PreTranslateMessage msg=101, haveCharMsg=0, Ret=0 120 CGetInputDlg::PreTranslateMessage msg=101, haveCharMsg=0 121 CMyEdit::OnKeyUp 122 CMyEdit::DefWindowProc msg=101, haveCharMsg=0 123 ~~CMyEdit::DefWindowProc msg=101, haveCharMsg=0, Ret=0 124 ~~CMyEdit::OnKeyUp 125 ~~CGetInputDlg::PreTranslateMessage msg=101, haveCharMsg=0, Ret=1 看到了吗?在103处(对应处理过程006处)的CGetInputDlg::PreTranslateMessage里产生了WM_CHAR消息,并放入了消息队列中。由此可见消息确实是WM_KEYDOWN处理过程中产生的。在WM_KEYDOWN处理完成后,消息循环便检测到它,并在PumpMessage中Get它,并进入110相应的处理。也就在此时队列中已无WM_CHAR消息了。 耶,不对啊,很多地方不是说TranslateMessage产生WM_CHAR消息?他们的意思也就是PumpMessage中的 ::TranslateMessage(&m_msgCur),我将PumpMessage简化如下: 200 BOOL CWinThread::PumpMessage() (CGetInputDlg::PumpMessage) 201 { 202 ASSERT_VALID(this); 203 if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)) 204 return FALSE; 205 // process this message 206 if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur)) 207 { 208 ::TranslateMessage(&m_msgCur); 209 ::DispatchMessage(&m_msgCur); 210 } 211 return TRUE; 212 } 这里可见,只有当PreTranslateMessage(CGetInputDlg::)返回FALSE时才会执行208处对应的函数,而事实上从103处可以看到CGetInputDlg::PreTranslateMessage还没有返回,就有了WM_CHAR消息,而且从~~CGetInputDlg::PreTranslateMessage可以看出PreTranslateMessage返回为1,不会执行208、209。那是不是那些说法错了呢? 也不一定,如果他指的是对话框程序,那我想他就确实错了,但对于非对话框程序是对的,我建了一SDI工程,PreTranslateMessage中对于非加速键返加是FALS,即会执行208、209。::TranslateMessage(&m_msgCur)来翻译键盘消息。当然,如果我想PreTranslateMessage中的默认处最终还是调TranslateMessage来完成翻译的,只是不对应208处的代码而己。 到此以也就差不多了,但点要提醒大家,如果你在验证这些时,在某些函数中设了断点(如OnKeyDown),然后按键输入,这时会停在断点处,然后你再运行,这时会得出某些不一样的结果。主要是WM_KEYUP消息没有了,因为收到WM_KEYUP消息的窗体为调试器。 |