开发者

Creating multiple dialogs in an MFC app with no main Window, they become children of each other

开发者 https://www.devze.com 2022-12-28 06:29 出处:网络
(title updated) Following on from this question, now I have a clearer picture what\'s going on... I have a MFC application with no main window, which exposes an API to create dialogs. When I call some

(title updated) Following on from this question, now I have a clearer picture what's going on...

I have a MFC application with no main window, which exposes an API to create dialogs. When I call some of these methods repeatedly, the dialogs created are parented to each other instead of all being parented to the desktop... I have no idea why.

But anyway even after creation, I am unable to change the parent back to NULL or CWnd::GetDesktopWindow()... if I call SetParent followed by GetParent, nothing has changed.

So apart from the really weird question of why Windows is magically parenting each dialog to the last one created, is there anything I'm missing to be able to set these windows as children of the desktop?


UPDATED: I have found the reason for all this, but not the solution. From my dialog constructor, we end up in:

BOOL CDialog::CreateIndirect(LPCDLGTEMPLATE lpDialogTemplate, CWnd* pParentWnd,
    void* lpDialogInit, HINSTANCE hInst)
{
    ASSERT(lpDialogTempla开发者_运维百科te != NULL);

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

    return CreateDlgIndirect(lpDialogTemplate, pParentWnd, hInst);
}

Note: if (pParentWnd == NULL)pParentWnd = AfxGetMainWnd();

The call-stack from my dialog constructor looks like this:

  • 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)

Running in the debugger, if I manually change pParentWnd back to 0 in CDialog::CreateIndirect, everything works fine... but how do I stop it happening in the first place?


Some thoughts:

First, you are passing NULL for the parent window the whole way through the chain. Its becomming non NULL when MFC tries to find your applications main window.

As I see it you have two mitigations:

  1. Create a CWnd from the desktop window. CWnd::GetDesktopWindow will give you a non NULL window to use as a parent window that will inhibit the AfxGetMainWnd call.
  2. Or trace into AfxGetMainWnd, find out where it is reading the main window from, and see if there is some setting to prevent it finding your dialog window.

On a final note. The MFC terminology is unfortunate :- On Windows, only child windows have parent windows. Popup or desktop windows have owner windows. CreateWindow takes a single parameter that accepts the owner, or parent of the window being created. The distinction is important because while a parent window can be changed, an owner cannot. SetParent will NOT change the owner window for a popup or overlapped window.


OK, found it!

There were actually two problems. I was passing NULL as the parent/owner... but trying to pass CWnd::GetDesktopWindow() was not helping so I'd given up on the idea until finding the behaviour of CDialog::CreateIndirect. That made me take a closer look at my code, and I finally spotted that MyDialog::MyDialog(CWnd *pParent) was calling super::Create(NULL), not super::Create(pParent)... because we'd always passed it NULL before anyway the bug had never been apparent.

So yet again, the hard problem turned out to be only one step away from a typo!


MFC can only create one window at a time IIRC. In the dark and distant past at least, when MFC creates a Win32 Window it needs to associate the MFC CWnd instance with the window. Because the first message a window receives is NOT the WM_CREATE message with the LPVUSERDATA parameter MFC stored the CWnd instance in a thread local variable - in the not unreasonable expectation that the next call to CWnd::WindowProc would be for the window it started trying to create.

I have no idea how one could actually write code to subvert this process. It all hinges on how you could be creating windows at differing velocities.

0

精彩评论

暂无评论...
验证码 换一张
取 消