3
votes

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.

1
I suspect you are leaking a window. This wouldn't appear as a memory leak in your application because windows are maintained by the system. Check the "USER objects" column in Task Manager to see if the value grows over time.arx

1 Answers

3
votes

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();
}