我该如何解决GetParent/EnumChildWindows不对称的问题?

4
我最近使用Microsoft的Spy++检查了一个GUI,并注意到了一个奇怪的结构; 它看起来像这样(警告,前面是ASCII艺术): | + 002004D6 "MyRootWindow1" FooClassName | | | + 001F052C "MyChildWindow" ClassOfChildWindow | \ 001D0A8C "MyRootWindow2" SomeOtherClassName 有两个根窗口,002004D6和001D0A8c,前者有一个子窗口001F052C。
现在,如果不是一件事情,这就很好:调用GetParent(或在Spy ++中观察“父窗口”或“所有者窗口”字段)子窗口(001F052C)会产生001D0A8C。
阅读:“MyChildWindow”是“MyRootWindow1”的子窗口,但“MyRootWindow1”不是“MyChildWindow”的父窗口。相反,“MyChildWindow”的父窗口是“MyRootWindow2”,但是,为了使这个完整,枚举“MyRootWindow2”的子窗口并不会得到“MyChildWindow”。
这是一个完全静态的GUI应用程序,因此这里没有任何竞争条件或其他问题。
有人知道这是如何发生的吗?有人知道我如何解决这个问题吗?直到现在,我使用GetParentEnumChildWindows来获取给定HWND的父(或子)窗口,并假设这种关系是对称的。也许还有其他我应该使用的东西吗?
编辑:这里是一个小的C++程序的代码,演示了这个问题:
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 );

请注意'child'对象具有WS_POPUPWS_OVERLAPPED设置,但没有WS_CHILD

子窗口缺少 WS_CHILD。此外,父窗口可能与所有者窗口不同(尽管这很少见)。看起来你可能正在为子窗口设置两者。你能详细说明一下你想要实现什么吗? - Adrian McCarthy
是的,缺少WS_CHILD。我在最后一句话中指出了这一点。不幸的是,所示代码不在我的控制之下。它是一个GUI应用程序的代码的一部分,我想要进行内省。 - Frerich Raabe
2个回答

3

当然,这并没有太多意义。它看起来像是子窗口正在重新定位自己。在.NET Windows Forms中,这是一种常见的技术,它有一个“停车窗口”,子控件可以在其容器窗口需要重新创建时找到一个临时家。这不是非常明显的效果,也是暂时的。

另一个可能性是SetParent()。它具有支持旧的Windows 3.x程序的appcompat行为。在SDK文档中对此进行了很好的解释,简而言之,窗口可以成为父级,但不能设置其WS_CHILD样式标志。Adobe Acrobat Reader是一个经典的例子。我不确定这对EnumChildWindows会产生什么影响。

最后但并非最不重要的:不要忘记Spy++给出了窗口的静态视图。按F5更新窗口列表可能很重要以跟踪更改。

并不是很好的解释。尝试找出活动的顶层窗口是否重要,我认为是。


谢谢您深入的回答!我也考虑过重新设置父级的问题,但我原本以为这会正确更新所有链接(因此重新设置父级将使旧父级的子级减少一个,新父级的子级增加一个)。我不会想到会出现这样的情况。 兼容性行为的想法非常有趣,我会看看能否找到任何相关资料! - Frerich Raabe
半相关的问题--是否有相对容易的方法来重新创建控件,还是唯一的方法是针对每种控件手动实现它? - user541686
据我所知,这似乎与此无关。但是我知道什么呢,为什么问我?@morechilli有批准的洞察力,我只是发布了哇!基努·里维斯的答案。 - Hans Passant

3

GetParent的文档解释如下: “请注意,尽管它的名字是这样的,但此函数可以返回所有者窗口而不是父窗口。”

由于您没有创建子窗口,所以我猜您遇到了这种情况。

您应该能够调用GetAncestor并传递GA_PARENT,正如文档中所说: “检索父窗口。这不包括所有者,就像使用GetParent函数一样。”

参见Win32窗口所有者与窗口父级?


这在我的情况下不起作用;GetAncestor(GA_PARENT)仍然返回错误的父级(示例代码中的oldParent)。问题在于SetParent()调用没有更新引用。 - Frerich Raabe
我有点困惑你想要实现什么 - 只有在调用SetParent之前,在子窗口上设置WS_CHILD标志才能使其工作。 - morechilli
是的,缺少WS_CHILD。我在最后一句话中指出了这一点。:-) 显示的代码不幸地不在我的控制之下。它是GUI应用程序的代码的一部分,我想要内省它。我试图实现的目标是能够获取任何给定HWND的父级(或子级),即使窗口层次结构像上面的代码一样被破坏。这可能意味着我必须使用Enum(Child)Windows来模拟GetParent,或者反过来。 - Frerich Raabe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接