• WM_CHAR消息分析


    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消息的窗体为调试器。
     
  • 相关阅读:
    NOIP2013 花匠解题报告
    (3) 深入理解SELinux SEAndroid(第三部分
    (2) 深入理解SELinux SEAndroid(第二部分)
    (1) 深入理解SELinux SEAndroid(第一部分)
    SELinux深入理解
    SELinux策略语言--类型强制(编写TE规则)
    Android 6.0中SELinux的TE简介
    8250_fintek
    run "setprop ctrl.start wpa_supplicant" manually
    AOSP Nougat
  • 原文地址:https://www.cnblogs.com/chengxin1982/p/1453132.html
Copyright © 2020-2023  润新知