EnumChildWindows already enumerates recursively


I often see people write code that goes something like this:

// do not use - see discussion
void DoSomethingToWindowTree(HWND hwndRoot)
{
 // first do it to the window passed in
 DoSomething(hwndRoot);
 // now do it to all the children
 EnumChildWindows(hwndRoot, DoSomethingHelper, 0);
}

BOOL CALLBACK DoSomethingHelper(HWND hwnd, LPARAM lParam)
{
 DoSomethingToWindowTree(hwnd);
 return TRUE;
}

The intent here was to perform the operation on all the windows in a window tree by operating on the root, then operating on each of the children. Operating on the children is in turn performed recursively, so that we eventually see every window in the tree.

Except that if you actually run this function on a vaguely interesting window tree, you'll find that items get counted multiple times.

The reason is that the EnumChildWindows function already does the recursion:

If a child window has created child windows of its own, EnumChildWindows enumerates those windows as well.

If you add your own recursion, then you end up counting grandchildren twice, great-grandchildren four times, and so on. The recursion is already done by EnumChildWindows; just use it.

void DoSomethingToWindowTree(HWND hwndRoot)
{
 // first do it to the window passed in
 DoSomething(hwndRoot);
 // now do it to all the descendants (children, grandchildren, etc.)
 EnumChildWindows(hwndRoot, DoSomethingHelper, 0);
}

BOOL CALLBACK DoSomethingHelper(HWND hwnd, LPARAM lParam)
{
 DoSomething(hwnd);
 return TRUE;
}
Comments (15)
  1. bj says:

    You mean like GetWindow with GW_CHILD?

  2. Pierre B. says:

    I thin what may be confusing people too is the formulation chosen: "has created child windows of its own."

    That sounds as if it depends how the child was created. It could easily be interpreted as meaning that child controls that were designed by the programmer are not enumerated, only child window that were automatically created (like a drop-down control’s menu or equivalent).

    Why not the simpler "has child of his own" or "enumerates the children recursively"?

    (Not that Raymond edits the documentation, but the error is comprehensible…)

  3. RichardRudek says:

    For those of you who were scratching their heads, like me, trying to find where the recursion was, it’s in the DoSomethingHelper() callback.

    I suppose this is one of those cases where your own instincts somehow make you blind to the things other people can do. Like making a recursive call from a Windows callback.

    Because I’ve been doing a lot of capture device work, recently, my initial reaction was, oh no,  wrong context. Then I realised that this is not a device-driver to user-code callback !

  4. Adam says:

    *sigh*

    Why is that all the way down in the "remarks" section of the documentation[0], and not in the synopsis? Just replacing the word "child" with the word "descendant" in each place of that paragraph would make it so much clearer.

    A parent’s grandchild (and great-grandchild, and great-great-grandchild, etc…) is not its child. It’s its descendant.

    Just out of curiosity, is there a function to Enumerate a window’s children, and only its children? Or do you use EnumChildWindows() and return early when the parent’s HWND is not the one you’re interested in?

    (I suppose there’s not much point in asking why the function wasn’t called EnumDescendantWindows() instead of EnumChildWindows() in the first place)

    [0] http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowfunctions/enumchildwindows.asp

  5. Oh *blush*… I just did that mistake a few weeks ago for a screen capture tool, and didn’t realize why I saw some windows more than once while browsing them.

    Thanks!

  6. PatriotB says:

    What a timely blog posting!  I don’t think I ever used EnumChildWindows before (but had heard of it), until yesterday when I was reviewing some code I inherited.  I noticed we were using EnumChildWindows to find a particular window, and wondered why it *was* finding it since that window wasn’t an immediate child of the passed-in window.  Debugging through I saw that it actually enumerated each child in the entire window tree.

    And the next day, Raymond writes a blog entry about it! :-)

  7. Kumar says:

    Unrelated to this topic. But I didnt know where

    else to ask for help ;)

    Any info on IThumbNailCapture and what shell object to cocreate?

  8. trial&error says:

    I’ve figured this out with trial & error. It’s counter-intuitive, but obvious when looking at the resulting hwnds.

    Looping through GetWindow() should be avoided, it isn’t atomic, and special care must be taken if the children is modified between calls.

  9. Sven Groot says:

    And the next day, Raymond writes a blog entry about it! :-)

    Must be those psychic powers at work again. :)

    I do think that EnumChildWindows is poorly named. Larry Osterman’s been blogging about software contracts, and the function name is an important part of the contract. He talks about the principle of least surprise, and a function called EnumChild that in fact enumerates descendants doesn’t follow that principle in my opinion.

    And what if MS wanted to introduce a function that really enumerates only direct children, what would they call it? EnumChildWindowdsButThisTimeForReal? EnumChildWindowsEx?

    Well, at least it’s mentioned in the docs, even though it is a bit burried.

  10. chrism says:

    Anyone with any familiarity with the MSDN docs knows to check the Remarks section before using a function. As soon as you see the name "EnumChildWindows" you should be thinking "does that mean strict child windows or all descendants?" Then you scroll down to the Remarks section and there’s your answer. Nothing obscure or difficult about it at all.

  11. Sebastian Redl says:

    As soon as you see the name "EnumChildWindows" you should be thinking "does that mean strict child windows or all descendants?"

    Only in the WinAPI. In any API that is not encumbered by two decades worth of cruft, you should be able to rely on the fact that children are children, and grandchildren are not.

  12. CoqBlog says:

    …elle s’en charge déjà. Comme d’autres, le blog de Raymond Chen, The Old New Thing , fait depuis longtemps

  13. zd says:

    If you add your own recursion, then you end up counting grandchildren twice, great-grandchildren four times, …

    great-grandchildren 3 times?

  14. Renfield says:

    The second code sample isn’t correct…

  15. zd says:

    Why? I can’t see anything wrong there.

Comments are closed.