【问题标题】:Creating multiple dialogs in an MFC app with no main Window, they become children of each other在没有主窗口的 MFC 应用程序中创建多个对话框,它们成为彼此的子级
【发布时间】:2025-12-21 17:25:11
【问题描述】:

(标题已更新) 继this 问题之后,现在我对发生的事情有了更清晰的了解......

我有一个没有主窗口的 MFC 应用程序,它公开了一个 API 来创建对话框。当我重复调用其中一些方法时,创建的对话框是彼此为父级的,而不是所有的父级到桌面...我不知道为什么。

但无论如何,即使在创建之后,我也无法将父级更改回 NULL 或 CWnd::GetDesktopWindow()...如果我调用 SetParent 然后调用 GetParent,则没有任何改变。

因此,除了为什么 Windows 神奇地将每个对话框作为父级创建的最后一个对话框这个非常奇怪的问题之外,我还缺少什么能够将这些窗口设置为桌面的子级吗?


更新:我找到了所有这一切的原因,但没有找到解决方案。从我的对话框构造函数中,我们最终得到:

BOOL CDialog::CreateIndirect(LPCDLGTEMPLATE lpDialogTemplate, CWnd* pParentWnd,
    void* lpDialogInit, HINSTANCE hInst)
{
    ASSERT(lpDialogTemplate != NULL);

    if (pParentWnd == NULL)
        pParentWnd = AfxGetMainWnd();
    m_lpDialogInit = lpDialogInit;

    return CreateDlgIndirect(lpDialogTemplate, pParentWnd, hInst);
}

注意:if (pParentWnd == NULL)pParentWnd = AfxGetMainWnd();

我的对话框构造函数的调用堆栈如下所示:

  • mfc80d.dll!CDialog::CreateIndirect(const DLGTEMPLATE * lpDialogTemplate=0x005931a8, CWnd * pParentWnd=0x00000000, void * lpDialogInit=0x00000000, HINSTANCE__ * hInst=0x00400000)
  • mfc80d.dll!CDialog::CreateIndirect(void * hDialogTemplate=0x005931a8, CWnd * pParentWnd=0x00000000, HINSTANCE__ * hInst=0x00400000)
  • mfc80d.dll!CDialog::Create(const char * lpszTemplateName=0x0000009d, CWnd * pParentWnd=0x00000000)
  • mfc80d.dll!CDialog::Create(unsigned int nIDTemplate=157, CWnd * pParentWnd=0x00000000)
  • MyApp.exe!CMyDlg::CMyDlg(CWnd * pParent=0x00000000)

在调试器中运行,如果我在 CDialog::CreateIndirect 中手动将 pParentWnd 改回 0,一切正常...但我如何首先阻止它发生?

【问题讨论】:

  • 调用 SetParent() 是否成功?
  • 您没有为原始问题提供任何其他信息。请不要重复问同一个问题,继续你原来的帖子。
  • 另一个问题的答案是“Windows 正在互相作为父母”。不过COM等的讨论有点乱。
  • 另外,当我将链接放入自己时,您不觉得“可能重复...”有点傻吗? :)

标签: mfc winapi


【解决方案1】:

一些想法:

首先,您在整个链中为父窗口传递 NULL。当 MFC 尝试查找您的应用程序主窗口时,它变为非 NULL。

在我看来,您有两个缓解措施:

  1. 从桌面窗口创建一个 CWnd。 CWnd::GetDesktopWindow 将为您提供一个非 NULL 窗口,用作禁止 AfxGetMainWnd 调用的父窗口。
  2. 或者跟踪到 AfxGetMainWnd,找出它从哪里读取主窗口,看看是否有一些设置阻止它找到你的对话窗口。

最后一点。 MFC 术语是不幸的:- 在 Windows 上,只有子窗口有父窗口。弹出窗口或桌面窗口具有所有者窗口。 CreateWindow 采用一个参数,该参数接受正在创建的窗口的所有者或父级。区别很重要,因为虽然父窗口可以更改,但所有者不能。 SetParent 不会更改弹出窗口或重叠窗口的所有者窗口。

【讨论】:

  • 感谢所有者与父母的澄清,我一直对差异感到困惑。
  • 虽然我的答案是“答案”,但你独立发现了同样的问题,所以你可以打绿色勾 - 谢谢!
【解决方案2】:

好的,找到了!

实际上有两个问题。我将 NULL 作为父/所有者传递......但试图传递 CWnd::GetDesktopWindow() 并没有帮助,所以我放弃了这个想法,直到找到 CDialog::CreateIndirect 的行为。这让我仔细查看了我的代码,我终于发现 MyDialog::MyDialog(CWnd *pParent) 正在调用 super::Create(NULL),而不是 super::Create(pParent)... 因为我们总是将它传递给 NULL 之前无论如何该错误从未出现过。

再次证明,难题离错别字只有一步之遥!

【讨论】:

    【解决方案3】:

    MFC 一次只能创建一个窗口 IIRC。至少在黑暗而遥远的过去,当 MFC 创建 Win32 窗口时,它需要将 MFC CWnd 实例与窗口相关联。因为窗口接收到的第一条消息不是带有 LPVUSERDATA 参数的 WM_CREATE 消息,所以 MFC 将 CWnd 实例存储在线程局部变量中 - 不合理地期望下一次调用 CWnd::WindowProc 将针对它开始尝试的窗口创建。

    我不知道如何真正编写代码来颠覆这一过程。这一切都取决于您如何以不同的速度创建窗口。

    【讨论】:

    • 实际上我测试了更多,即使我添加延迟也可以重现。请参阅我更新的帖子。我没有父窗口的事实似乎导致 Windows 找到了最好的。