views:

443

answers:

3

Long-delayed update

I'm accepting MUG4N's answer to this question, and I also want to respond to some of the criticisms that were raised against it.

ChrisF said:

...you can't make UI calls directly from background threads.

This is a blanket statement, and is not 100% true. Let me just point out a few facts:

  1. You can actually make UI calls all you want if you set Control.CheckForIllegalCrossThreadCalls = false. "Ack!" I hear you saying. "Don't ever do that!" Yes, yes -- but why? The answer: because sometimes this will corrupt memory.

    The control classes in System.Windows.Forms are not written to be thread-safe, so sometimes updating them from background threads can corrupt memory. But if this only sometimes happens and not always, what this tells me is that it is not the calling of UI code per se, but rather the potentially unsafe collision of UI code that can cause exceptions.

  2. To reinforce point 1, consider this: the "safe" way to invoke UI code from a background thread is to do so using Control.Invoke or Control.BeginInvoke, right? But this is a UI call; it's just the UI call we're supposed to make if we're updating the GUI from a non-GUI thread. What I mean is, clearly, it is not simply invoking "any" method on a Control object from an outside thread that's going to cause chaos (if that were the case, then we couldn't even call Invoke and we'd be stuck completely). Again, it's the potential collision of separate UI calls that cannot safely occur simultaneously that will prove destructive.

  3. Keeping the above two points in mind, ask yourself: why would it be unsafe to call MessageBox.Show from a non-GUI thread? A completely separate Form is created and displayed; its properties do not in any way interact with any other existing GUI object; in fact, it cannot be accessed anywhere in any manner, except for one: from the calling thread, which accesses its DialogResult property (and only that via the Show method's return value).

Moving along. Conrad Albrecht said:

...given the assertion that Show() sets up its own message pump in Dan's ref'd topic, (which was not substantiated, but which I can't refute)...

This is a totally fair point (though I personally hold Jared Par in high enough esteem that I wouldn't generally be inclined to doubt what he says). In any case, a peek at the MessageBox.Show method through Reflector reveals this snippet:

Application.BeginModalMessageLoop();
try
{
    result = Win32ToDialogResult(SafeNativeMethods.MessageBox(new HandleRef(owner, zero), text, caption, type));
}
finally
{
    Application.EndModalMessageLoop();
    UnsafeNativeMethods.ThemingScope.Deactivate(userCookie);
}

A further peek into the Application.BeginModalMessageLoop method reveals this:

ThreadContext.FromCurrent().BeginModalMessageLoop(null);

And this ThreadContext.FromCurrent, in turn:

// [Reflector shows that currentThreadContext is a ThreadStatic member. -Dan]
if (currentThreadContext == null)
{
    currentThreadContext = new Application.ThreadContext();
}
return currentThreadContext;

I don't know enough about these lower-level Windows constructs to fully understand this code, but this seems to me to be evidence of exactly what Jared was saying in the answer I referenced in my old comment (for curious readers: Does MessageBox.Show() automatically marshall to the UI Thread?).

So, yeah. I am totally in agreement with MUG4N on this one.

(If anyone can convincingly argue that I am still mistaken here, please speak up. Although I feel I've made a pretty good case for why I believe MUG4N is right, I'm obviously not 100% certain.)


Original question

Often you just want to notify the user that something has occurred, but there's really no need for any input from them. In this common scenario, I sometimes see code like this:

MessageBox.Show("Something has occurred", "Something", MessageBoxButtons.OK);

This code, as we all know, causes a little pop-up window to appear with only an OK button. Now here's the thing: this code blocks (the UI thread). But in the vast majority of cases, it seems to me, if you only have an OK button, there's very little need to block. (Isn't the purpose of blocking typically to receive some input from the user? And if the user's only choice is "OK," in this typical case, isn't blocking pretty pointless?)

Obviously I could just write my own little form that does basically exactly what MessageBox.Show does, except that it returns nothing (no DialogResult) and doesn't block. But I was just wondering if something like this exists already that I didn't know about.

+4  A: 

Hi, you need to use Multi Threading to Perform this task in which one thread (Main Thread) will do processing and other will be used to show the messagebox

MUG4N
@MUG4N: That's one approach. But `MessageBox.Show` will still block the thread from which it's called, right? I guess what you're saying is I could just write a new method that calls `MessageBox.Show` from a background thread, is that right?
Dan Tao
@Dan - you can't make UI calls directly from background threads.
ChrisF
@ChrisF: In my experience, you **can** call `MessageBox.Show` from a background thread. I realize this probably isn't encouraged, but I haven't found any documentation indicating that it should absolutely not be done. I actually upvoted this answer after testing it out myself. It worked -- *without* setting `Control. CheckForIllegalCrossThreadCalls` to `true`.
Dan Tao
ChrisF++, had to downvote the answer.
Conrad Albrecht
@Conrad: Seriously? Did you *try* what MUG4N suggested? It works perfectly. Furthermore, according to my research, it seems that calling `MessageBox.Show` from a background thread is actually *perfectly fine*. See, for instance, Jared Par's answer to this question: http://stackoverflow.com/questions/559252/does-messagebox-show-automatically-marshall-to-the-ui-thread
Dan Tao
It does work with second thread!
MUG4N
"It works" when you happen to test it is no vindication for threading bugs, which fail unpredictably. But, given the assertion that Show() sets up its own message pump in Dan's ref'd topic, (which was not substantiated, but which I can't refute) I'll retract my downvote. [or I would retract if StacO would let me]
Conrad Albrecht
+4  A: 

What about adding a NotifyIcon to your application and displaying a balloon tip? The down side is that the notification will disappear after a short time, but maybe that's best for your users if they don't need to take action.

There are more suggestions on this question.

Don Kirkby
+1 In my mind, this is really more a question of UI metaphors than it is about the threading behaviors of particular widge classes. If you're looking for non-modal behavior with an alert, something other than a MessageBox seems more appropriate.
Marc Bollinger
+2  A: 

What I would try is call the MessageBox function from the Win32 API directly, like :

using System.Runtime.InteropServices;

[DllImport("User32.dll")]
public static extern int MessageBox(int h, string m, string c, int type);

Try using a null handle, and the APPLMODAL type. That may work.

Francisco Soto