What's with this MSH_MOUSEWHEEL message? - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20080806-00/?p=21353
Raymond Chen 2008年06月06日
MSH_MOUSEWHEEL 消息是怎么回事?
硬件团队正在研发一种鼠标滚轮设备,并需要一种方法让应用程序支持这种鼠标。一种方法是说:“我们将开始销售这种带滚轮的鼠标,但在下一代Windows发布之前,没有任何应用程序可以使用它。”当然,这意味着要等到Windows NT 4发布,而谁也不知道那会是什么时候。此外,这意味着人们必须升级Windows才能利用他们的新鼠标。可以想象,他们对“等几年”的计划并不满意。
在此期间,他们为应用程序响应鼠标滚轮提出了一种临时机制。于是引入了zmouse.h
头文件及其注册消息MSH_MOUSEWHEEL
。当你安装滚轮鼠标驱动程序时,它会监听硬件的滚轮事件,并在鼠标滚轮转动时发布这个新消息,应用程序只需响应WM_MOUSEWHEEL
消息(如果运行在支持该消息的Windows版本上)或MSH_MOUSEWHEEL
消息(如果运行在不支持该消息的旧版Windows上)。不幸的是,这两个消息的行为不同,所以不是简单地编写:
if (uMsg == WM_MOUSEWHEEL || uMsg == g_msgWheel) {
// 处理滚轮事件
}
(下面几段总结了MSDN中已经明确的内容;如果已经知道这些消息的工作原理,可以跳过。)
首先,让我们看看WM_MOUSEWHEEL
。这个消息被传递到拥有焦点的窗口(在SetFocus
的意义上)。如果窗口过程不处理这个消息,只是将其传递给DefWindowProc
函数,那么DefWindowProc
函数会将这个消息转发给窗口的父窗口。通过这种方式,WM_MOUSEWHEEL
消息自动地从焦点窗口“向外冒泡”到父窗口链,直到有人最终处理这个消息(或者根本没有被处理)。
另一方面,MSH_MOUSEWHEEL
消息从外部向内部工作。它被传递到前景窗口(在SetForegroundWindow
的意义上)。如果窗口过程不想处理这个消息,它可以将消息转发给它选择的子窗口,直到其中一个返回TRUE
表示消息已被处理,或者直到没有更多的候选者。
我将以表格形式总结这些差异,因为人们似乎非常喜欢表格。
WM_MOUSEWHEEL | MSH_MOUSEWHEEL | |
---|---|---|
传播方向 | 内向外 | 外向内 |
传播机制 | DefWindowProc | SendMessage |
处理 | 自动 | 手动:应用程序检查从子窗口返回的值以确定接下来要做什么 |
如果已处理的返回值 | 零 | TRUE |
如果未处理的返回值 | DefWindowProc | FALSE |
注意WM_MOUSEWHEEL
更简单,内向外的传播机制保留了其他消息(如WM_CONTEXTMENU
和WM_SETCURSOR
)的精神。
为什么MSH_MOUSEWHEEL
不能以相同的方式进行?
首先,MSH_MOUSEWHEEL
没有修改DefWindowProc
函数的特权。毕竟,引入这个消息的整个目的,是因为我们正试图向一个早于鼠标滚轮的旧操作系统添加滚轮支持。换句话说,如果我们能修改DefWindowProc
来处理MSH_MOUSEWHEEL
消息,那么我们一开始就不需要MSH_MOUSEWHEEL
消息;我们只需要修改DefWindowProc
来处理WM_MOUSEWHEEL
消息。
前一段中的论点是一个令人沮丧的常见问题。面对问题X和变通方法Y,有人会问:“为什么不用方法Z?”然而,如果你看看方法Z,你会发现它受到与问题X完全相同的问题。
这里有一个“困惑的变通方法”的真实世界例子:
“由于I-90桥关闭了,我不能乘坐550公交车从贝尔维尤到安全球场。相反,我将乘坐230到雷德蒙德,然后换乘545。”
——嗯,那很傻。为什么不乘坐245到东门,然后换乘554呢?这样更快。
“嗯,554也使用I-90桥。”
好的,所以你不能更改DefWindowProc
,但为什么不至少从内向外传播MSH_MOUSEWHEEL
而不是从外向内呢?
从焦点窗口开始的假设是你可以找出焦点窗口是什么,但如果你注意到了《每个Win32程序员都应该知道的五件事》,你就会知道每个线程都有自己的焦点窗口。(不是吹毛求疵的真,但足够真。)因此,当注入MSH_MOUSEWHEEL
消息的帮助程序调用GetFocus
时,它只会得到自己的焦点窗口,而不是控制前景窗口的线程的焦点窗口。(记住,我们谈论的是1996年,远在GetGUIThreadInfo
函数发明之前。历史爱好者可以更多地了解鼠标滚轮的发明者。)由于内部向外是不可能的,这基本上就迫使了外部向内部。
现在你知道鼠标滚轮消息是如何工作的,你可以解释这位客户看到的行为:
我注意到
WM_MOUSEWHEEL
消息被传递到了错误的子窗口。 我有一个父窗口和两个子窗口。 即使我把鼠标指针移动到子窗口1上,WM_MOUSEWHEEL
却发送到了子窗口2。