How can a dialog become responsive while waiting for a call to DoModal() to return?

866 views Asked by At

A button on a dialog causes a child dialog to be created, and displayed modally.

e.g:

void ParentDialog::OnButton()
{
 ChildDialog dlg;
 int ret = dlg.DoModal();
}

The parent dialog is initially unresponsive as expected. But, the child dialog also makes COM calls to a server module, which causes the server to make COM calls back to the parent dialog... to update displayed data, etc. In one case, this causes the parent dialog to suddenly become responsive again even though the child dialog is still on screen. Both dialogs can now be interacted with even though the parent dialog is still patiently waiting for OnButton()to return.

How can this happen? I'm trawling the code but is there anything specific I should be looking for?

2

There are 2 answers

2
Roman Ryltsov On BEST ANSWER

The dialog has its own message pump/loop and inside the call it has a loop where it keeps receiving and dispatching window messages. This includes COM related messages worker windows receive and convert to COM callbacks you are seeing. Once the dialog is closed, respective windows are destroyed, the function exits from the loop and returns control to your code.

That is, without returning control to your code immediately, window message dispatching still keeps working as usually and UI is responsive.

MSDN:

... The function displays the dialog box, disables the owner window, and starts its own message loop to retrieve and dispatch messages for the dialog box.

When the dialog box procedure calls the EndDialog function, DialogBox destroys the dialog box, ends the message loop, enables the owner window (if previously enabled), and returns the nResult parameter specified by the dialog box procedure when it called EndDialog.

0
Barmak Shemirani On

You can create a modeless dialog and use RunModalLoop to wait for the dialog to finish, somewhat similar to DoModal()

void CMyWnd::foo()
{
    static BOOL isOpen = FALSE;
    if (isOpen) return;//optional, prevents opening multiple dialogs

    CDialog dlg;
    dlg.Create(IDD_DIALOG1, this);
    dlg.ShowWindow(SW_SHOW);
    isOpen = TRUE;

    int result = dlg.RunModalLoop(0);
    dlg.DestroyWindow();
    isOpen = 0;

    TRACE("res:%d\n", result);
}

Problem: Note that in the above example you can close the program but the dialog will still be up. You have to force the dialog close, the above function doesn't deal with that.

Or you can create modeless dialog the usual way:

if (m_Dlg) return;
m_Dlg = new CDialog
m_Dlg.Create(IDD_DIALOG1, this);
m_Dlg.ShowWindow(SW_SHOW);

However in this example if user clicks OK/Cancel then you don't know about it, you have to override OnOK()/OnCancel then send message to parent Window/Dialog, process message, and then destroy the dialog manually.