Matteo Italia
Matteo Italia

Reputation: 126787

Why is mouse capture released if I disable a window but not if I disable its parent?

I have a Win32 application with a main window and a child window; in some circumstances, the child window captures the mouse using SetCapture.

In these circumstances, if I disable the child window using EnableWindow(hChild, FALSE) I immediately stop receiving mouse events. However, if I disable its parent using EnableWindow(hMainWindow, FALSE), the child window keeps receiving events as long as the mouse is captured. Once it's released, the child window regularly stops receiving events until the main window is enabled again.

Why this difference? Aren't child windows disabled as well when any of their parents is disabled?

(inspired by a real problem we had today at work, given that there's not much discussion about SetCapture & co. I hope this will help someone in future)

Upvotes: 2

Views: 583

Answers (1)

Matteo Italia
Matteo Italia

Reputation: 126787

Aren't child windows disabled as well when any of their parents is disabled?

When you disable a window, its child windows technically aren't disabled—even if the parent window is disabled, if you call IsWindowEnabled over any of them you'll get TRUE, and they won't have the WS_DISABLED style. However, they are disabled in practice, as normally they won't receive any input.

This comes from how the parent window being disabled affects the "regular" input events dispatch: when Windows has to dispatch a mouse event, it looks recursively for the "most nested" window whose client area contains the cursor1, but stops when it meets a disabled window, and doesn't recurse over its children. This ensures that even the children of a disabled window normally don't receive any input events.

However, when a window captures the mouse, regular dispatch is bypassed: all mouse events will be dispatched straight to the capturing window, regardless of whether it has a disabled parent, or even if it's disabled itself.

But wait: this doesn't match the observation:

if I disable the child window using EnableWindow(hChild, FALSE) I immediately stop receiving mouse events.

This comes from the fact that EnableWindow doesn't just set the WS_DISABLED style, but does some additional magic; in particular

If the window is being disabled, the system sends a WM_CANCELMODE message.

This message was introduced to ask to the receiving window to cancel any "temporary" mode, such as open menus or captured mouse, in preparation to disable the window to e.g. display a dialog box:

For example, the system sends this message to the active window when a dialog box or message box is displayed. Certain functions also send this message explicitly to the specified window regardless of whether it is the active window. For example, the EnableWindow function sends this message when disabling the specified window.

These tasks are accomplished by the default window procedure:

When the WM_CANCELMODE message is sent, the DefWindowProc function cancels internal processing of standard scroll bar input, cancels internal menu processing, and releases the mouse capture.

so, if you were so inclined, you could disable this behavior completely by handling WM_CANCELMODE explicitly in your window procedure, bypassing the default processing (although I wouldn't recommend it). In that case, disabling the child window itself would keep the mouse captured exactly as in the EnableWindow(hMainWindow, FALSE) case.


So, ultimately the difference arises from the fact that EnableWindow does this extra trick only for the window that is being disabled, not for its children, so if any of them is still capturing the mouse, it'll remain captured.


Notes

  1. Roughly speaking; the exact process is a bit more complicated, most importantly there's z-order to keep into account, invisible windows are ignored, "layered" windows need special processing, ...

Upvotes: 5

Related Questions