8

I'm executing a code in a worker thread. Sometimes, I need to show a dialog or a Message.

I have been playing with the code and only seems to be strictly necesary to Invoke when I pass a IWin32Window to the dialog. Otherwise works fine.

My questions are two:

  1. Must I call with an Invoke?
  2. What risks I have if I show the dialog or the messagebox without Invoke?

Thanks in advance

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
Daniel Peñalba
  • 30,507
  • 32
  • 137
  • 219
  • What happens if the main thread decides to throw up a modal dialog? – David Heffernan Aug 09 '11 at 10:05
  • @David: if too threads trying to show dialog using `Invoke` the last thread will wait until the first dialog returned. – Jalal Said Aug 09 '11 at 10:23
  • @Jalal No it won't. The background thread runs its own modal message loop to handle the dialog. – David Heffernan Aug 09 '11 at 10:26
  • @David: `No it won't. The background thread runs its own modal message loop to handle the dialog.` when using `Invoke` in each of threads they are both executing the code at the UI thread. – Jalal Said Aug 09 '11 at 10:29
  • @Jalal Yes, when using Invoke. But the question is about what happens when Invoke is not called and the modal dialog message loop runs on the background thread. – David Heffernan Aug 09 '11 at 10:32

3 Answers3

11

It is a bit of a bug in Winforms. It contains diagnostic code in the Handle property getter that verifies that the property is used in the same thread as the one that created the handle. While extremely helpful to diagnose threading bugs, that is not always appropriate. One such case is here, Windows doesn't actually require that the parent of a window is owned by the same thread.

You can work around it by pinvoking SetParent() or by temporarily disabling checking with Control.CheckForIllegalCrossThreadCalls. Or by using Control.Invoke(), the best way. Do not work around it by not specifying the owner. For lack of another window, the dialog's owner is the desktop window. It will have no Z-order relationship with the other windows that have the desktop as their owner. And that will make the dialog disappear behind another window occasionally, completely undiscoverable by the user.

There's a bigger problem though, displaying dialogs on threads is a nasty usability problem. Shoving a window into the user's face while she's working with your program is an all-around bad idea. There's no telling what will happen when she's busy clicking and typing. Her accidentally closing the dialog without even seeing it is a real danger. Don't do it.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • +1, I agree he should not do it, I was just answering the question though. – Jalal Said Aug 09 '11 at 10:39
  • 2
    @Jalal - yes, sorry. It is a trap worth mentioning. – Hans Passant Aug 09 '11 at 10:49
  • @HansPassant with 'Windows doesn't actually require that the parent of a window is owned by the same thread.' that I can pass in an owner to the `MessageBox.Show` method from a non-UI thread and not have to worry? So, from some background thread I do `MessageBox.Show(someForm, ...)` and that is okay? Or do I need to test for `InvokeRequired`? The code in which I am showing the message box is not tied to a UI component/form... – MoonKnight Nov 04 '13 at 14:05
1

If you are not specifying the owner of the MessageBox it will work because it doesn't rely on the form UI thread.
I think it is safe to call it without using Invoke if you are not aware of showing as a dialog to specific form.

Edit: To test it, the following code just creating a new thread and show message box from it where at UI thread creating a second message at the same time with specifying the owner to be the form, both messages shows up without any problem:

private void button1_Click(object sender, EventArgs e)
{
    new Thread(() =>
    {
        MessageBox.Show("Hello There!");
    }) { IsBackground = true }.Start();

    Thread.Sleep(1000);

    if (MessageBox.Show(this, "Hi", "Jalal", MessageBoxButtons.YesNo) == 
        System.Windows.Forms.DialogResult.Yes)
    {
        return;
    }
}
Jalal Said
  • 15,906
  • 7
  • 45
  • 68
0

When I need to interact with some controls, created in main thread from another thread I use BackgroundWorker. It has event called OnProgressChange. So maeby try create this BackgroundWorker object in main thread, and in DoWork method just fire this event, then method attached to it will be executed in main thread, so you have access to all controls.