WindowFromPoint, ChildWindowFromPoint, RealChildWindowFromPoint, when will it all end?


Oh wait, there's also ChildWindowFromPointEx.

There are many ways of identifying the window that appears beneath a point. The documentation for each one describes how they work, but I figured I'd do a little compare/contrast to help you decide which one you want for your particular programming problem.

The oldest functions are WindowFromPoint and ChildWindowFromPoint. The primary difference between them is that WindowFromPoint returns the deepest window beneath the point, whereas ChildWindowFromPoint returns the shallowest.

What do I mean by deep and shallow?

Suppose you have a top-level window P and a child window C. And suppose you ask one of the above functions, "What window is beneath this point?" when the point is squarely over window C. The WindowFromPoint function looks for the most heavily nested window that contains the point, which is window C. On the other hand ChildWindowFromPoint function looks for the least nested window that contains the point, which is window P, assuming you passed GetDesktopWindow as the starting point.

That's the most important difference between the two functions, but there are others, primarily with how the functions treat hidden, disabled, and transparent windows. Some functions will pay attention to hidden, disabled, and/or transparent windows; others will skip them. Note that when a window is skipped, the entire window hierarchy starting from that window is skipped. For example, if you call a function that skips disabled windows, then all children of disabled windows will also be skipped (even if the children are enabled).

Here we go in tabular form.

Function Search Hidden? Disabled? Transparent?¹
WindowFromPoint Deep Skip Skip It's Complicated²
ChildWindowFromPoint Shallow Include Include Include
ChildWindowFromPointEx Shallow Optional Optional Optional
RealChildWindowFromPoint Shallow Skip Include Include³

The return values for the various ...FromPoint... functions are the same:

  • Return the handle of the found window, if a window was found.
  • Return the handle of the parent window if the point is inside the parent window but not inside any of the children. (This rule obviously does not apply to WindowFromPoint since there is no parent window passed into the function.)
  • Otherwise, return NULL.

The entries for ChildWindowFromPointEx are marked Optional because you, the caller, get to specify whether you want them to be skipped or included based on the CWP_* flags that you pass in.

¹There is a lot hiding behind the word Transparent because there are multiple ways a window can be determined transparent. The ...ChildWindowFromPoint... functions define transparent as has the WS_EX_TRANSPARENT extended window style.

²On the other hand, WindowFromPoint defines transparent as returns HTTRANSPARENT in response to WM_NCHITTEST. Actually, that's still not true. If the window belongs to a process thread different from the one calling WindowFromPoint, then WindowFromPoint will not send the message and will simply treat the window as opaque (i.e., not transparent).

³The RealChildWindowFromPoint includes transparent windows in the search, but has a special case for group boxes: The RealChildWindowFromPoint function skips over group boxes, unless the return value would have been the parent window, in which case it returns the group box after all.

Why is RealChildWindowFromPoint so indecisive?

The RealChildWindowFromPoint function was added as part of the changes to Windows to support accessibility. The intended audience for RealChildWindowFromPoint is accessibility tools which want to return a "reasonable" window beneath a specific point. Since group boxes usually enclose other controls, RealChildWindowFromPoint prefers to return one of the enclosed controls, but if the point belongs to the group box frame, then it'll return the group box.

One place I see confusion over the various ...WindowFromPoint... functions is code which uses one of the functions, and then massages the result, unaware that there is already a function that returns the pre-massaged result for you. For example, I've seen code which calls WindowFromPoint followed by GetAncestor(GA_ROOT). This does a pointless down-and-up traversal of the window tree, searching for the deepest window that lies beneath the specified point, then walking back up the tree to convert it to a shallow window. This is the Rube Goldberg way of calling ChildWindowFromPointEx(GetDesktopWindow(), ...).

Next time, a look at the mysterious RealGetWindowClass function. What makes this function more real?

Comments (21)
  1. Dan Bugglin says:

    I wanted to make a screenshot tool that would overlay the screen with a screenshot of the screen, then draw outlines as you moved over each desktop window then crop the screenshot to that window when you clicked.

    It worked great, except that WindowFromPoint (or Child*, or whatever I was using) ended up always, correctly, returning the overlay window covering the desktop.  D'oh!

    Ended up having to EnumDesktopWindows, creating an array of window bounds, and walking through this array on every mouse move to figure out which rectangle to draw based on mouse location.  It works, but I'm wondering now if there was an easier solution.

  2. Dan Bugglin says:

    Now that I think about it, I might have used WindowFromPoint, and then used GetAncestor or a related API call when I realized it didn't just work on top level windows.  I didn't know about ChildWindowFromPoint… anyways very useful compare and contrast of all the functions, thanks!

  3. Guest says:

    Wouldn't it be easier to have one function that parameterizes the type of search to conduct, and whether to include hidden, disabled, and transparent windows?

  4. ArousedBoat says:

    @Guest: Great idea. If only there was a table summarizing all the information so I could write one… Oh well… better get back to work on that time machine.

  5. Gabe says:

    The "It's Complicated" link is funny because the Facebook status is the first thing I thought about when I saw the heading in the table. The linked-to article is from May of 2009, yet is already out of date because it doesn't mention Widowed, Separated, or Divorced as options. I guess those would fall under either Single or It's Complicated back then.

    Honestly, I didn't even realize those new options existed until I saw an old classmate's status set to Widowed one day. When I noticed that his profile picture had just changed to a newborn baby, it was probably the saddest thing I've ever seen on Facebook.

  6. Joshua Ganes says:

    I've never specifically used these functions, but I was surprised to read your explanation and find that my initial assumptions about the ChildWindowFromPoint function were exactly the opposite of what you describe.

  7. Roastbeef says:

    Now if this could only be included in MSDN…

    [The information is already in MSDN. All I did was organize it into a table, because it seems there are some people who understand things better when they are placed in a table. -Raymond]
  8. pete.d says:

    "The WindowFromPoint function looks for the most heavily nested window that contains the point, which is window C. On the other hand ChildWindowFromPoint function looks for the least nested window that contains the point, which is window P"

    Huh?  So, the function with the name "Child" in it retrieves the parent window P, and the function without the name "Child" in it retrieves the child window C?

    Surely that's not correct.

    [You conveniently deleted the part of the sentence that clears up your confusion.-Raymond]
  9. John Muller says:

    The child function (if I recall correctly) has a parameter for specifying the parent window, while the basic version does not. Looking at the full signatures makes it more obvious.

  10. Roman says:

    "The information is already in MSDN. All I did was organize it into a table,.."

    Well, here's what the MSDN library has to say about WindowFromPoint:

    ~~~

    The return value is a handle to the window that contains the point. If no window exists at the given point, the return value is NULL. If the point is over a static text control, the return value is a handle to the window under the static text control.

    The WindowFromPoint function does not retrieve a handle to a hidden or disabled window, even if the point is within the window. An application should use the ChildWindowFromPoint function for a nonrestrictive search.

    ~~~

    While it coincides with what you said about disabled and hidden windows, it has nothing about HTTRANSPARENT or different processes.

    [Okay, the HTTRANSPARENT part isn't there. But I didn't think that's what the person was complaining about. -Raymond]
  11. Timothy Byrd says:

    I, too, glossed over the "assuming you passed GetDesktopWindow as the starting point" – and it made my brain hurt.

    Thanks for writing this up, Raymond – and happy new year to you.

  12. Roastbeef says:

    I wasn't complaining so much as wishing that, in general, MSDN would apply that little extra bit of effort (like the table) that makes these things much easier to understand.

  13. Crescens2k says:

    @Roastbeef

    The problem is, the MSDN is just a documentation source for each individual function. So usually the MSDN would just describe each function and not make a comparison to another one unless there was a good reason.

    I suppose they could add a new page to the about windowing or using windowing section which lists the differences, but the individual functions explain precisely how each function works so the information is already there to start with. So this added section would not provide anything which isn't already in the documentation.

    One of the tough parts about the MSDN is getting the information you want/need out of the mountain of information it gives you. That can be tough at times.

  14. .dan.g. says:

    @pete.d

    I too was instantly confused reading the explanation and I'm not entirely sure that the 'missing' part of the sentence actually clarifies things.

    [Rename the windows. Desktop = P, top-level window = C1, child window = C2. Does it make more sense now? -Raymond]
  15. James Schend says:

    @Guest: Sure, but since Microsoft can't erase the old functions for compatibility reasons, you'd just be adding *yet another* point of confusion to the process of picking a function.

  16. pete.d says:

    "Rename the windows. Desktop = P, top-level window = C1, child window = C2. Does it make more sense now?"

    I suppose. If I now understand correctly, the "Child…" function returns the _immediate_ qualifying child. The non-"Child…" function also return a child window, but searches for the qualifying child farthest from the root of the window-ownership tree.

    In other words, both functions actually could have been named "<Something>ChildWindowFromPoint". Like, "ImmediateChildWindowFromPoint" and "FurthestRemovedChildWindowFromPoint".  Granted, those names are a bit wordy.  :)  But they would have had better mnemonic value.

    [Right. Deep vs. shallow. At some point you have to give up trying to encode all semantic information in the function name and assume people will actually read the documentation. -Raymond]
  17. Worf says:

    Hrm.

    If we make a simplifying assumption that child windows are smaller (or equal in size to) the parent window, WindowFromPoint returns the smallest window that contains the point. ChildWindowFromPoint takes in a parent window, and returns the largest child window OF THAT PARENT that encloses the point. In WFP, if the point ends up on the desktop window, it returns NULL. In CWFP, if the point is outside of the passed parent window OR the parent window, it returns NULL.

    Basically, WFP gives you the window handle of what's there under the mouse. CWFP returns the immediate child of the passed in window that contains the point in question – here, "child" refers to the fact that it returns an immediate child of the parent window ONLY, and not grand children or great-grand-children or deeper.

    So if we created windows like:

    Desktop (always there)

    WindowMyApp (main app window)

    DialogMyApp (a dialog your app created)

    ButtonMyApp (a button in the dialog of your app)

    and click within ButtonMyApp, WFP will return ButtonMyApp. CWFP will return any, depending on the window handle passed in. If you passed in GetDesktopWindow(), you will get WindowMyApp. If you passed in WindowMyApp, you get DialogMyApp. If you passed in DialogMyApp, you get back ButtonMyApp.

    Whew. I think that will clarify what "child" means.

    [Well, mostly correct. Child windows can be larger than parents (they will be clipped); your simplification fails to handle the case where the point is outside any children but is inside the parent; and in your sample window hierarchy, the dialog box is not a child window; it's an owned window. -Raymond]
  18. Neil says:

    I remember having the same problem as Dan, but comments will probably close before I manage to find my source code to see how I ended up doing it.

  19. kero says:

    2 Raymond

    [quote]On the other hand, WindowFromPoint defines transparent as returns HTTRANSPARENT in response to WM_NCHITTEST. Actually, that's still not true. If the window belongs to a process different from the one calling WindowFromPoint, then WindowFromPoint will not send the message and will simply treat the window as opaque (i.e., not transparent).[/quote]

    Raymond, it is not necessarily – "PROCESS different from the one calling WindowFromPoint".

    Just – different THREAD.

    Here is my old little demo just on the topic "…WindowFromPoint…":

    http://www.wasm.ru/…/attachment.php (wndfrompt.rar).

    [I stand corrected. Thanks. -Raymond]
  20. Neil says:

    So, it turns out I ended up rolling my own loop that recursed through the visible children finding the most deeply nested child that contained the point, but, unless nothing else was available, excluding child windows without the clip siblings style, presumably to ignore group boxes, since they preceded their groups in the window order.

    And yes, the code is as unreadable as the above sentence.

  21. Neil says:

    Oh, and the reason I didn't have to worry about my overlay window because this was Windows 3.0 and you could do silly stuff such as setting the capture to an invisible window and drawing on the desktop's window DC (as opposed to the desktop window's DC, of course).

Comments are closed.