How can I work around the GetParent/EnumChildWindows asymmetry?
I recently inspect a GUI with Microsoft's Spy++ and noticed a strange structure; it looked like this (warning, ASCII art ahead):
| + 002004D6 "MyRootWindow1" FooClassName | | | + 001F052C "MyChildWindow" ClassOfChildWindow | \ 001D0A8C "MyRootWindow2" SomeOtherClassName
There are two root windows, 002004D6
and 001D0A8c
, the former one of which has one child window, 001F052C
.
Now, this would be all good and find if it wasn't for one thing: calling GetParent (or watching the 'Parent Window' or 'Owner Window' fields in Spy++) on the child window (001F052C
) yields 001D0A8C
.
Read: "MyChildWindow" is a child of "MyRootWindow1", but "MyRootWindow1" is not the parent of "MyChildWindow". Instead, the parent of "MyChildWindow" is "MyRootWindow2" - but, to make this complete, enumerating the children of "MyRootWindow2" does not yield "MyChildWindow".
This is a perfectly static GUI applications, so there are no race conditions here or anything.
Does anybody know how this can happen? Does anybody know how I can work around this? Until now, I used GetParent and EnumChildWindows to get the parent (or children) for a given HWND, and I assumed that this relationship is symmetrical. Is there maybe something else I should be using?
EDIT: Here's the code for a small C++ program which demonstrates the problem:
const HINSTANCE thisModule = ::GetModuleHandle( NULL );
HWND oldParent = ::CreateWindow( TEXT("STATIC"),
TEXT("Old parent"),
WS_VISIBLE | WS_BORDER,
0, 0, 850, 500,
NULL,
NULL,
thisModule,
开发者_开发知识库 NULL );
HWND child = ::CreateWindow( TEXT("STATIC"),
TEXT("This is a sample dialog"),
WS_OVERLAPPED | WS_POPUP | WS_VISIBLE | WS_BORDER,
100, 100, 300, 300,
oldParent,
NULL,
thisModule,
NULL );
HWND newParent = ::CreateWindow( TEXT("STATIC"),
TEXT("Fake main window"),
WS_VISIBLE | WS_BORDER,
0, 0, 850, 500,
NULL,
NULL,
thisModule,
NULL );
::SetParent( child, newParent );
Note how the 'child' object has WS_POPUP
and WS_OVERLAPPED
set, but not WS_CHILD
.
The documentation for GetParent explains: "Note that, despite its name, this function can return an owner window instead of a parent window. "
As you are not creating a child window i'm guessing you hit this case.
You should be able to call GetAncestor passing GA_PARENT as the documentation says: "Retrieves the parent window. This does not include the owner, as it does with the GetParent function."
See Win32 window Owner vs window Parent?
Well, that doesn't make a lot of sense of course. It smells like the child window is re-parenting itself. It is a common technique in .NET Windows Forms, it has a "Parking window" where child controls can find a temporary home when their container window needs to be recreated because a window style changed. This isn't a very visible effect, it is also temporary.
Another, remote, possibility is SetParent(). It has appcompat behavior to support old Windows 3.x programs. It is explained pretty well in the SDK docs for it, in a nutshell a window can be parented but not set its WS_CHILD style flag. Adobe Acrobat Reader is a classic example of a program that does this. What effect that will have on EnumChildWindows is unclear to me.
Last but not least: don't forget that Spy++ gives a static view of the windows. Pressing F5 to update the window list can be important to track changes.
Not great explanations. Try to find out if it matters which toplevel window is active, I suspect it does.
精彩评论