none
Closing a CDialog from another thread

    Question

  • Hello,

    I have a CDialog representing a progress bar and a task running in a background thread and I try to close the CDialog when the task is over.

    But for some reason, calling CDialog::OnCancel() from the thread trigger an exception in CDialog::DestroyWindow() (I cannot investigate it because wincore.cpp has not been compiled in debug mode). I tried to call SendMessage(WM_DESTROY) but it did not work. The call to SendMessage(WM_CLOSE) works but it uses the handler I defined to intercept the Cancel button which is not the best solution. So, what's the correct way to close a CDialog from another thread?

    Thanks in advance,
    Mark
    Thursday, March 08, 2018 11:16 PM

Answers

  • Windows and their related message queues are connected to the thread that creates them.  So when you call DestroyWindow (or do any number of things) from a thread other than the one that created the window bad things happen.

    PostMessage is safe because it just puts a message in a window's message queue and returns.

    SendMessage is a little more complicated.  When you use SendMessage to send a message to a window from a different thread the system actually switches threads to perform the processing and the sending thread is blocked until the receiving thread processes the message.  Care must be taken to ensure that you don't end up in a deadlock if SendMessage is used.

    • Marked as answer by Mark531 Friday, March 09, 2018 9:10 PM
    Friday, March 09, 2018 8:48 PM

All replies

  • I assume that the dialog displaying the progress bar is a modeless dialog.  Using WM_CLOSE is certainly an acceptable solution.  Even if you did not have a handler, the WM_CLOSE message would be processed by the default window procedure which would initiate the window destruction process.  Another acceptable alternative would be to define a private message to be sent from the thread to the progress dialog which would handle it by calling DestroyWindow().
    Friday, March 09, 2018 12:09 AM
  • >So, what's the correct way to close a CDialog from another thread?

    Don't.

    Use a signal to have the dialog close itself from the UI thread it runs on.

    One way to do that would be to have the worker thread post (using PostMessage) a user defined message (see
    https://msdn.microsoft.com/en-us/library/windows/desktop/ms644930(v=vs.85).aspx) to the dialog window, and in the handler of that
    message (https://msdn.microsoft.com/en-us/library/k35k2bfs.aspx) the dialog can close itself.

    Dave

    Friday, March 09, 2018 12:13 AM
  • You are using CDialog so that means you are using MFC. See Using Registered Message Ids. That is an alternative to user defined messages and very much like them. An alternative is to use a named event (CEvent Class). Named events can be used across processes but I don't think I have tried that. My Events for Threads is about using an unnamed event between threads.

    Microsoft certainly recommends sending a WM_CLOSE message to close a window; see How To Terminate an Application "Cleanly" in Win32. My memory of MFC is hazy but I think that it is not normal to intercept the Cancel button in OnClose (WM_CLOSE). Perhaps the code can be modified to be more consistent with other CDialog applications.



    Sam Hobbs
    SimpleSamples.Info

    Friday, March 09, 2018 11:40 AM
  • You could define a custom message and handle it in your dialog. From your thread call PostThreadMessage().

    -Seetharam

    Friday, March 09, 2018 1:39 PM
  • You could define a custom message and handle it in your dialog. From your thread call PostThreadMessage().

    The dialog would never see the custom message.  PostThreadMessage posts a message to the message queue of the target thread but the hwnd of the message is NULL.  Therefore, the receiving thread's message loop would never dispatch such a message to the dialog window.
    Friday, March 09, 2018 1:53 PM
  • RLWA32, I basically said the same thing you mentioned above - "Another acceptable alternative would be to define a private message to be sent from the thread to the progress dialog which would handle it by calling DestroyWindow()."

    -Seetharam


    • Edited by Seetharam Friday, March 09, 2018 3:18 PM
    Friday, March 09, 2018 3:17 PM
  • RLWA32, I basically said the same thing you mentioned above -

    No, it's not the same.  PostThreadMessage won't work.

    As far as private messages go, do you really see any benefit in repeating suggestions previously made?

    Friday, March 09, 2018 3:26 PM
  • Ok, I'll got with the WM_CLOSE then. The problem I had with that message is that it triggered a handler I defined for my Cancel button. But I found out that I had associated it with the IDCANCEL identifier, and it should be special in MFC. I'll change the identifier.

    Friday, March 09, 2018 8:26 PM
  • Sure, I could use as well a user-defined message. But do you know why the DestroyWindow() triggers an exception when it's not called from the window's thread? I found this explanation on a forum, but I spent days trying to understand what was going on...
    Friday, March 09, 2018 8:28 PM
  • Windows and their related message queues are connected to the thread that creates them.  So when you call DestroyWindow (or do any number of things) from a thread other than the one that created the window bad things happen.

    PostMessage is safe because it just puts a message in a window's message queue and returns.

    SendMessage is a little more complicated.  When you use SendMessage to send a message to a window from a different thread the system actually switches threads to perform the processing and the sending thread is blocked until the receiving thread processes the message.  Care must be taken to ensure that you don't end up in a deadlock if SendMessage is used.

    • Marked as answer by Mark531 Friday, March 09, 2018 9:10 PM
    Friday, March 09, 2018 8:48 PM
  • Ok, thank you for the explanation, I'll send a WM_CLOSE message through PostMessage then.
    Friday, March 09, 2018 9:10 PM
  • It is interesting that you chose to solve the problem with WM_CLOSE but the chosen post says nothing about WM_CLOSE. Of course I have a personal motivation to say that since I was the one that posted the most about WM_CLOSE specifically.


    Sam Hobbs
    SimpleSamples.Info

    Friday, March 09, 2018 11:29 PM
  • I assume that the dialog displaying the progress bar is a modeless dialog.  Using WM_CLOSE is certainly an acceptable solution.  Even if you did not have a handler, the WM_CLOSE message would be processed by the default window procedure which would initiate the window destruction process.  Another acceptable alternative would be to define a private message to be sent from the thread to the progress dialog which would handle it by calling DestroyWindow().
    The first response to the OP's question discusses WM_CLOSE as shown above.

    • Edited by RLWA32 Friday, March 09, 2018 11:38 PM
    Friday, March 09, 2018 11:37 PM