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消息的窗体为调试器。