离奇失踪的WM_HOTKEY消息--浅析WIN32消息队列
故事的开端有些平淡,眼红于XXX小程序,认为写完该程序就有了和心仪的妹子多相处的机会,必须搞,必须酷,按钮不能有,界面得隐藏,这就想到了全局快捷键。
注册调用RegisterHotKey(m_hWnd, 300, MOD_ALT, 'K');
定义消息处理函数 afx_msg long OnHotKey(WPARAM wparam, LPARAM lparam);
消息关联处理程序ON_MESSAGE(WM_HOTKEY, OnHotKey)
定义消息处理函数 ,switch(wparam)调用对应的函数处理。
测试了一下,一切OK,开始搜索其他API,埋头苦整,一番恶战终于搞定其他功能,开始登记其他快捷键并调用处理过程。OH SHIT!快捷键没效果了?难道是登记的快捷键冲突了?检查了RegisterHotKey的返回值,是正确的,那么登记快捷键肯定没问题!该怎么办呢?去查WIN32消息处理机制来解决问题吧
MFC总共有三种类型的消息:窗口、命令、控件通知,嗯,找找窗口消息是怎么处理的。
登记全局快捷键的原理解释是:某键被按下时,系统在所有的热键列表中寻找匹配者,匹配成功将WM_HOTKEY消息发送给登记了该热键的线程的消息处理队列。挠了挠脑袋,我认定WM_HOTKEY已经发送给指定的消息队列。
-----补充知识点,发送消息有两种形式:发送消息,即时到达并立马调用目标窗口的进程,目标窗口必须为调用函数返回一个结果才能继续;寄送消息,将消息加入目标进程的消息队列,应用程序有空闲时就会搜索消息队列,从消息队列中删除消息并将消息发送给指定窗口,通信可能延迟。鼠标和键盘消息由于其特殊性,采用寄送的方式处理,其他所有消息都是发送的方式。
到这里就有点悟了,敢情我定义的全局快捷键处理函数OnHotKey并不会直接被调用,大胆猜测一下:窗口进程从消息队列中取出寄送来的WM_HOTKEY消息后首先交给了窗口,而窗口线程收到WM_HOTKEY消息并没有按照我的意愿调用OhHotKey。再找一下窗口处理消息的机制。
寄送消息在被消息泵弹出之前会一直保留在消息队列中,直到应用程序调用GetMessage函数从消息队列中将之取出,取出后会调用PreTranslateMessage和TranslateMessage两个函数进行消息翻译,翻译后的消息通过DispatchMessage调用该消息预期的目标窗口进程。看到这里,不由得笑了,这可是大有玄机啊,GO ON!
PreTranslateMessage有点眼熟,果然从代码里找到这个知其然、不知其所以然的预处理函数。埋头继续查:绝大多数本窗口的消息都要经过PreTranslateMessage处理,如果想在MFC之前处理某消息,可以重载该函数,重点来了,只有经过消息队列的消息才会经过PreTranslateMessage处理,即时发送的消息或其他不经过消息队列的消息不会理睬该函数,联系前面的寄送消息方式,各位小看官都应该懂了,快捷键消息到了这里被截下来没有发送给预期的窗口处理函数。挖,还得挖,这个坑有点苗头了。
PretranslateMessage的定义和返回值仔细的看:是否调用TranslateMessage和DispatchMessage消息向指定窗口发送消息由PreTranslateMessage的返回值决定,当返回值为TRUE的时候,不回把消息发送给对应的窗口函数处理,这难道是真相?预处理消息的时候挖坑把WM_HOTKEY消息给埋起来了?修改代码,拦截到指定的消息后返回FALSE果然解决了问题。
随着真相的浮出水面,我又一次期待着想象中的其乐融融,加油,我会做一个颇具高手风范的tool。