Search code examples
winapivisual-c++mfcmodal-dialogmodeless

CDialog - EndDialog from a MODELESS dialog?


The MS documentation (and others) "clearly" states:

... Because the normal OnOk and OnCancel member functions of a CDialog object would call EndDialog, make sure your modeless dialog box does not call those functions and instead overrides

Since CDialog::OnOk effectively calls CDialog::EndDialog, and that method looks like:

void CDialog::EndDialog(int nResult)
{
    ASSERT(::IsWindow(m_hWnd));

    if (m_nFlags & (WF_MODALLOOP|WF_CONTINUEMODAL))
        EndModalLoop(nResult);

    ::EndDialog(m_hWnd, nResult);
}

we can also check the docs for ::EndDialog which again "clearly" state:

Dialog boxes created by the DialogBox, DialogBoxParam, DialogBoxIndirect, and DialogBoxIndirectParam functions must be destroyed using the EndDialog function. An application calls EndDialog from within the dialog box procedure; the function must not be used for any other purpose.

Yet, I have a CDialog derived class that has it's default behavior wrt. OnOKand seemingly everything is working when I use it non-modal / modeless.

That is: * When I close the (modeless) dialog, it is closed/removed from view. * The application doesn't show any memory leaks. (MFC debug build)

So what? Do I need to prevent EndDialog and call DestroyWindow myself or not?


Note: I know what the docs and "the web" says. It's just that I haven't yet found why I need to do it differently, and this one class should be usable for modeless and modal mode, so not having to do anything different might be handy.


Solution

  • The MSDN Docs for CDialog::OnOK clearly states

    If you implement the OK button in a modeless dialog box, you must override the OnOK method and call DestroyWindow inside it. Do not call the base-class method, because it calls EndDialog which makes the dialog box invisible but does not destroy it

    So you would need to override CDialog::OnOK and call DestroyWindow() inside -- here's a modified example from MSDN:

    class CDlg : public CDialog
    {
        ...
        BOOL m_bModal;
        ...
    }
    
    CDlg::CDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CDlg::IDD, pParent)
    {
        ...
        m_bModal = FALSE;
        ...
    }
    
    INT_PTR CDlg::DoModal()
    {   m_bModal = TRUE;
        const INT_PTR rval = CDialog::DoModal();
        m_bModal = FALSE;
        return rval;
    }
    
    void CDlg::OnOK() 
    { 
       if (!UpdateData(TRUE)) 
       {
          TRACE(_T("UpdateData failed during dialog termination\n"));
          // The UpdateData routine will set focus to correct item
          return;
       }
       if (m_bModal)
           EndDialog(IDOK);
       else
           DestroyWindow();
    }
    
    void CDlg::OnCancel()
    {
       if (m_bModal)
           EndDialog(IDCANCEL);
       else
           DestroyWindow();
    }