views:

1593

answers:

4

I have a question about modeless dialog, I want to show an dialog on the screen, then it will display some information in it.

However if I used the following way, it has some problems: function() { showdialog(XXX). //heavy work. update the dialog.. //heavy work. update the dialog... }

it seems the dialog displayed ,but it do not draw any information in it , It only draw all information when the function is over. How can I modify the modeless dialog so, it will display the information immediately?

A: 

There are a few things you can do.

(1) You could post the dialog a message from inside the CDialog::OnInitDialog method and then handle the long function in the message handler of that posted message. That way the dialog will first be displayed and then later the long function will get run.

(2) The second option is to make sure the message loop gets some processing time. So if your long function is some sort of loop just add the occasional call to the ProcessMessages to make sure the message queue is kept empty:

void ProcessMessages()
{
    MSG msg;
    CWinApp* pApp = AfxGetApp();
    while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
    {
        pApp->PumpMessage();
    }
}

Edit: It certainly is possible to use threads is such a situation, but doing so is not always without risk and complexity.

Using threads with a GUI means having to deal with multiple message queues which then means using API's like PostThreadMessage and that introduces a new set of issues to be wary of.

For an example of one such issue refer to this link:

http://msdn.microsoft.com/en-us/library/ms644946(VS.85).aspx

where is says:

Messages sent by PostThreadMessage are not associated with a window. As a general rule, messages that are not associated with a window cannot be dispatched by the DispatchMessage function. Therefore, if the recipient thread is in a modal loop (as used by MessageBox or DialogBox), the messages will be lost. To intercept thread messages while in a modal loop, use a thread-specific hook.

I use the process message approach in the Zeus IDE and it works very well at making sure the GUI remains responsive to the user. It is also has the advantage of being very easy to implement.

jussij
IMO the ProcessMessage() function is the way to add responsiveness to a MFC app.
Hapkido
I too would use the ProcessMessage option ;)
jussij
I strongly disagree. 1) A UI method should not be performing a lot of "other" work, the concerns should be separated. 2) A simple worker thread can be used in most of these situations, no need for multiple pumps. Why are you trying to scare the OP away from threads?
Aidan Ryan
Did you read my reply? When you mix threads and GUIs windows messages can and do go missing if the code is not correct. Why mess with message hooks if you don't need to?Add in COM and you get another layer of complexity: http://msdn.microsoft.com/en-us/library/ms693344(VS.85).aspx
jussij
I've been there, done that and it is not the walk in the park you seem to think it is. For most case processing the message queue works just as well and it is very simple solution. As I said I use it all the time and it works just fine. And I say again it is very very simple to implement.
jussij
Windows messages do not "go missing." COM has nothing to do with this question. A worker thread for computations that posts to the UI is the standard, tried-and-true way to accomplish the OP's goal.
Aidan Ryan
ajryan writes: Windows messages do not "go missing."Can you read: "if the recipient thread is in a modal loop (as used by MessageBox or DialogBox), the messages will be lost"Once again try reading: "To intercept thread messages while in a modal loop, use a thread-specific hook."
jussij
ajryan writes: A worker thread for computations that posts to the UI is the standardIf it is so simple, stock standard and staight forward, why not post some source code showing how a MFC dialog would use a thread to load during OnInitDialog. Or do you just like talking out of your arse?
jussij
Re: if recipient is in a modal loop, the title of this question is "... modeless dialog."
Aidan Ryan
Also, no need for PostThreadMessage. You're making a simple problem more complicated than it is. And there's no need to get personal.
Aidan Ryan
+2  A: 

As a rule of thumb, heavy computations should never be placed in the GUI thread. Since it is a modeless dialog, the dialog will not own the message loop. The ProcessMessage() solution will work, but is IMO not the right way. My suggestion is: 1) Spawn a new thread in OnInitDialog() 2) Have the separate thread post messages to the dialog when something interesting happens. One of these interesting things is that the work is done.

Note, however, that this will mean that you need to perform proper synchronization.

erikkallen
+3  A: 

In OnInitDialog, start a worker thread to perform the computations. Post a user message from the worker thread to update the dialog.

This is superior to the ProcessMessages implementation for several reasons:

  • The code for doing the calculations can be separated out of the UI code, where it does not belong.

  • The UI remains responsive while the actual calculations are being performed. ProcessMessages allows multiple UI updates during the single calculation function, but the UI will still be blocked during the actual calculations.

Dialog code:

#define WM_NEW_COUNT (WM_USER + 0x101)

BEGIN_MESSAGE_MAP()
    ON_MESSAGE(WM_NEW_COUNT, OnNewCount)
END_MESSAGE_MAP()

BOOL CMyDialog::OnInitDialog()
{
    CWinThread* pThread = AfxBeginThread(MyCountFunc, this->GetSafeHwnd());
    return TRUE;
}

LRESULT CMyDialog::OnNewCount(WPARAM wParam, LPARAM)
{
    int newCount = (int)wParam;

    // m_count is DDX member, e.g. for label
    m_count = newCount;

    UpdateData(FALSE);

    return 0;
}


The worker thread:

UINT MyCountFunc(LPVOID lParam)
{
    HWND hDialogWnd = (HWND)lParam;

    for (int i=0; i < 100; ++i)
    {
        PostMessage(hDialogWnd, WM_NEW_COUNT, i, NULL);
    }
}
Aidan Ryan
A: 

Don't try to do your heavy work all at once. Have the dialog post itself a message in the WM_APP range in OnInitDialog. The WM_APP handler can do part of the heavy work, then do another PostMessage and return. In this way, you allow the message pump to process window messages in between your chunks of processing.

Mark Ransom