BUG: MFC Dialog Explicitly Owned by the Desktop Window Disables the Desktop (257812)



The information in this article applies to:

  • Microsoft Visual C++, 32-bit Enterprise Edition 5.0
  • Microsoft Visual C++, 32-bit Enterprise Edition 6.0
  • Microsoft Visual C++, 32-bit Professional Edition 5.0
  • Microsoft Visual C++, 32-bit Professional Edition 6.0
  • Microsoft Visual C++, 32-bit Learning Edition 6.0

This article was previously published under Q257812

SYMPTOMS

When you attempt to explicitly specify the desktop as the owner window for a modal MFC dialog, this action results in a system that does not respond to mouse events. If you click on different controls in the dialog box or on other windows on the desktop, there is no effect. To exit, you must dismiss the dialog box through the keyboard command.

CAUSE

The MFC code for the CDialog::DoModal function simulates modal dialogs by disabling the owner window of the dialog box. Under normal circumstances, this window is either an application window or sometimes NULL. The MFC code for CDialog::DoModal fails to test whether the dialog's owner is the desktop before MFC disables it.

RESOLUTION

The easiest workaround is to not explicitly specifying the desktop as the owner of a modal dialog. Instead, accept the default NULL argument which works fine as a default in most cases. However, if you want the desktop to own your dialog then you can override CDialog::DoModal in your dialog class implementation. Note that this is a somewhat difficult task.

STATUS

Microsoft has confirmed that this is a bug in the Microsoft products that are listed at the beginning of this article.

MORE INFORMATION

Steps to Reproduce Behavior

  1. Create a dialog-based application, Prj1, and accept all the defaults.
  2. Go to the CPrj1App::InitInstance function and change the line of code that declares the dialog box variable to read as follows:
          // Explicitly specify the owner of this dialog to be the desktop.
          CPrj1Dlg dlg(CWnd::FromHandle(::GetDesktopWindow()));  
    						
  3. Build and run the application.
  4. Click the mouse anywhere, and notice you do not get any response.
  5. Press ALT+TAB until you bring the focus back to the dialog application and then press Enter or Escape.

Override CDialog::DoModal

To work around the problem, provide an override for CDialog::DoModal. Do this by inserting the following code into your application:

    // XMN: Includes and defines needed to compile DoModal().
    // For AfxHookWindowCreate() and AfxUnhookWindowCreate()
    #include <AfxPriv.h> //danger, danger, including private MFC header.

    // From Mfc\Src\StdAfx.h.
    #ifndef _AFX_OLD_EXCEPTIONS
        #define DELETE_EXCEPTION(e) do { e->Delete(); } while (0)
    #else   //!_AFX_OLD_EXCEPTIONS
        #define DELETE_EXCEPTION(e)
    #endif  //_AFX_OLD_EXCEPTIONS
    // XMN: Includes and defines needed to compile DoModal().

    int CPrj1Dlg::DoModal() 
    {
        // Can be constructed with a resource template or InitModalIndirect.
        ASSERT(m_lpszTemplateName != NULL || m_hDialogTemplate != NULL ||
            m_lpDialogTemplate != NULL);

        // Load resource as necessary.
        LPCDLGTEMPLATE lpDialogTemplate = m_lpDialogTemplate;
        HGLOBAL hDialogTemplate = m_hDialogTemplate;
        HINSTANCE hInst = AfxGetResourceHandle();
        if (m_lpszTemplateName != NULL)
        {
            hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG);
            HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG);
            hDialogTemplate = LoadResource(hInst, hResource);
        }
        if (hDialogTemplate != NULL)
            lpDialogTemplate = (LPCDLGTEMPLATE)LockResource(hDialogTemplate);

        // Return -1 in case of failure to load the dialog template resource.
        if (lpDialogTemplate == NULL)
            return -1;

        // Disable parent (before creating dialog).
        HWND hWndParent = PreModal();
        AfxUnhookWindowCreate();
        BOOL bEnableParent = FALSE;

        // XMN: Replace next line:
        // If (hWndParent != NULL && ::IsWindowEnabled(hWndParent))
        // XMN:    with this line:
        if (hWndParent != NULL && hWndParent != ::GetDesktopWindow() && ::IsWindowEnabled(hWndParent))
        {
            ::EnableWindow(hWndParent, FALSE);
            bEnableParent = TRUE;
        }

        TRY
        {
            // Create modeless dialog.
            AfxHookWindowCreate(this);
            if (CreateDlgIndirect(lpDialogTemplate,
                            CWnd::FromHandle(hWndParent), hInst))
            {
                if (m_nFlags & WF_CONTINUEMODAL)
                {
                    // Enter modal loop.
                    DWORD dwFlags = MLF_SHOWONIDLE;
                    if (GetStyle() & DS_NOIDLEMSG)
                        dwFlags |= MLF_NOIDLEMSG;
                    VERIFY(RunModalLoop(dwFlags) == m_nModalResult);
                }

                // Hide the window before enabling the parent, etc.
                if (m_hWnd != NULL)
                    SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW|
                        SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
            }
        }
        CATCH_ALL(e)
        {
            DELETE_EXCEPTION(e);
            m_nModalResult = -1;
        }
        END_CATCH_ALL

        if (bEnableParent)
            ::EnableWindow(hWndParent, TRUE);
        if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)
            ::SetActiveWindow(hWndParent);

        // Destroy modal window.
        DestroyWindow();
        PostModal();

        // Unlock free resources as necessary.
        if (m_lpszTemplateName != NULL || m_hDialogTemplate != NULL)
            UnlockResource(hDialogTemplate);
        if (m_lpszTemplateName != NULL)
            FreeResource(hDialogTemplate);

        return m_nModalResult;
    }
				

Modification Type:MajorLast Reviewed:12/11/2003
Keywords:kbBug kbDlg kbpending KB257812