bug分享 - 收不到WM_NCLBUTTONUP消息

来源:互联网 发布:如何撤销域名备案 编辑:程序博客网 时间:2024/04/30 12:31

重载windows的ncarea来自绘窗口标题栏是条死路(在Vista系统上,如果主题带有毛玻璃效果,那么无论你的窗口标题栏如何自绘,都会把系统的标题栏覆盖在自绘的标题栏上),因此自绘标题栏的唯一办法就是在客户区模拟非客户区,"伪造"实现标题栏的功能。我的这篇文章有些误人。这篇文章的目的只是为了讲述以前曾经遇到的一个问题。

NCLBUTTONUP表示non-client left button up, 窗口的非客户区(也就是标题栏以及边框区域)的鼠标左键点击被释放的消息。

 

旺旺的网页型插件是一个在独立进程里的内嵌IE控件的普通窗口,为了让窗口的风格尽量和旺旺的风格一致,因此我重画了窗口的标题栏和最小化按钮等(另外一种办法就是干脆不要标题栏,

直接自己在客户区里仿造标题栏)。

 

最小化按钮的处理是响应WM_NCLBUTTONUP消息,收到该消息并且鼠标在最小化图标内,则将窗口最小化。

 

今天同事发现网页型插件的窗口的最小化按钮点击后,不太灵。我找了找原因,发现当在标题栏上点击鼠标左键后,能收到WM_NCLBUTTONDOWN(按下的消息),但是不一定能收到WM_NCLBUTTONUP

的消息(释放左键的消息)。Google了一下,发现Windows的确有这个问题(MSDN干脆完全就没说明)。当在非客户区点击鼠标左键并释放时,系统会发送窗口的WM_LBUTTONUP(注意不是WM_NCLBUTTONUP,这是客户区的鼠标释放消息,但是我们现在在非客户区释放鼠标如之奈何)消息到线程的消息队列,但线程的消息队列压根就不把这个消息发送给这个窗口的窗口过程!

 

既然不发送到窗口过程,那哥们儿是怎么发现的呢?他是通过spy++仔细看产生的消息发现的(因为spy++截取窗口的原理是通过使用HOOK的方式,而我的代码里为了处理IE的热键也用到了HOOK,

但是由于粗心没有针对所有消息都调用CallNextHookEx,以致于我用spy++看的时候没有这个哥们儿牛逼,调的半死,被工具陷害,造化弄人(因为spy++也是hook,因为这个消息被我的hook独占了,它老兄就hook不到了,因此也就看不见这个WM_LBUTTONUP消息了)

 

解决的办法基本原理就是当鼠标左键在非客户区按下释放后,自己伪造发送一条WM_NCLBUTTONUP消息给窗口过程。

因此要解决的核心问题是: 怎么判断当前鼠标左键在非客户区按下并释放了 (如果窗口过程收到了WM_NCLBUTTONDOWN,并且接着消息HOOK的回调函数里发现了WM_LBUTTONUP,就是表明

当前鼠标在非客户区按下并且释放了,此时伪造WM_NCLBUTTONUP消息)

 

解决办法如下:

1.       响应非客户区鼠标按下的消息WM_NCLBUTTONDOWN 定义一个flag, m_bNCLButtonDowned = true; 表示当前在非客户区按下了鼠标左键

2.       使用WH_GETMESSAGEHOOK, hook整个线程的消息队列,从而得到在非客户区释放鼠标时的WM_LBUTTONUP消息(因为spy++就是这种方式,因此我们也可以采用这种方式,当然google里的那哥们使用的是mouse hook,忒复杂了些)

3.       hook回调函数里的WM_LBUTTONUP消息判断当前m_bNCLButtonDowned == true?非客户区里是否按下了鼠标左键,如果是,则PostMessage一个WM_NCLBUTTONUP,将真正的非客户区的

鼠标释放消息发送给窗口的窗口过程

4.       在窗口过程里的WM_NCLBUTTONUP的处理函数里判断m_bNCLButtonDowned == true?如果是,则执行代码,同时置位m_bNCLButtonDowned,表示当前已经释放了鼠标左键,因此鼠标左键没有按下

 

前辈试验出来的系统会发送WM_NCLBUTTONUP的时刻:

1.       双击标题栏

2.       在客户区按住鼠标左键,但是在非客户区释放鼠标左键