为什么禁用窗口会释放鼠标捕获,但禁用其父窗口不会释放鼠标捕获?

Mat*_*lia 2 c windows mouse winapi event-handling

我有一个带有主窗口和子窗口的 Win32 应用程序;在某些情况下,子窗口使用SetCapture.

在这些情况下,如果我使用禁用子窗口,EnableWindow(hChild, FALSE)我会立即停止接收鼠标事件。但是,如果我使用 禁用其父EnableWindow(hMainWindow, FALSE)窗口,则只要捕获鼠标,子窗口就会继续接收事件。一旦它被释放,子窗口就会定期停止接收事件,直到主窗口再次启用。

为什么会有这种差异?当他们的父母被禁用时,子窗口不是也被禁用吗?

(受到我们今天在工作中遇到的一个实际问题的启发,因为没有太多关于SetCapture& co 的讨论。我希望这会对将来的人有所帮助)

Mat*_*lia 5

当他们的父母被禁用时,子窗口不是也被禁用吗?

当你禁用一个窗口时,它的子窗口在技术上并没有被禁用——即使父窗口被禁用,如果你调用IsWindowEnabled它们中的任何一个你都会得到TRUE,并且它们不会有WS_DISABLED样式。然而,它们在实践中被禁用,因为通常它们不会收到任何输入。

这来自被禁用的父窗口如何影响“常规”输入事件调度:当 Windows 必须调度鼠标事件时,它会递归查找客户区包含光标1的“最嵌套”窗口,但在遇到时停止禁用的窗口,并且不会对其子项进行递归。这确保即使是禁用窗口的子窗口通常也不会收到任何输入事件。

然而,当一个窗口捕获鼠标时,常规调度被绕过:所有鼠标事件都将直接调度到捕获窗口,无论它是否有禁用的父窗口,或者即使它本身被禁用。

但是等等:这与观察结果不符:

如果我使用禁用子窗口,EnableWindow(hChild, FALSE)我会立即停止接收鼠标事件。

这是因为EnableWindow它不仅设置了WS_DISABLED样式,还做了一些额外的魔法;特别是

如果窗口被禁用,系统会发送一条WM_CANCELMODE消息。

引入此消息是为了要求接收窗口取消任何“临时”模式,例如打开菜单或捕获鼠标,以准备禁用窗口以显示对话框:

例如,当显示对话框或消息框时,系统会将此消息发送到活动窗口。某些函数还会将此消息显式发送到指定的窗口,而不管它是否是活动窗口。例如,该EnableWindow函数在禁用指定窗口时发送此消息。

这些任务由默认窗口过程完成:

WM_CANCELMODE发送消息时,DefWindowProc功能取消标准滚动条输入的内部处理,取消内部菜单处理,并释放鼠标捕获。

因此,如果您愿意,可以通过WM_CANCELMODE在窗口过程中显式处理来完全禁用此行为,绕过默认处理(尽管我不建议这样做)。在这种情况下,禁用子窗口本身将保持鼠标完全捕获的EnableWindow(hMainWindow, FALSE)情况。


所以,最终的区别在于,EnableWindow这个额外的技巧只针对被禁用的窗口,而不是它的子窗口,所以如果它们中的任何一个仍在捕获鼠标,它将保持捕获状态。


笔记

  1. 大致说来; 确切的过程有点复杂,最重要的是要考虑z-order,忽略不可见的窗口,“分层”窗口需要特殊处理,......