Why did the BeginInvoke dispatcher crash when Control BeginInvoke succeeded in a C # Windows Forms application?

I initially tried using the BeginInvoke method of the Dispatcher class to show a message box in the main UI thread in my C # Windows Forms application. When I used this method, a message box did not appear. I set a breakpoint inside the body of the delegate that I passed to BeginInvoke (), and it never hit. I tried using the Action delegate and the MethodInvoker delegate. In any case, you're out of luck.

When I used the BeginInvoke method belonging to the Form object, it worked fine. Why does the Dispatch version fail (no exceptions or error messages)? Below are two different versions.

Dispatcher dispatcher = Dispatcher.CurrentDispatcher; // THIS FAILED. CONTEXT: Executing on worker thread. MethodInvoker theMethod = new MethodInvoker(delegate() { string msg = "Show this message on the main UI thread."; MessageBox.Show(msg, "Message"); }); dispatcher.BeginInvoke(theMethod); this.BeginInvoke(theMethod); // --------------------------------------------------- // THIS WORKED. CONTEXT: Executing on worker thread. MethodInvoker theMethod = new MethodInvoker(delegate() { string msg = "Show this message on the main UI thread."; MessageBox.Show(msg, "Message"); }); // "this" is a Form object. this.BeginInvoke(theMethod); 
+4
source share
1 answer

If I read your comments correctly, you call Dispatcher.CurrentDispatcher from a thread other than the UI. This does not mean that it is intended to be used.

As the documentation for Dispatcher.CurrentDispatcher says:

Gets the dispatcher for the current thread and creates a new dispatcher, if not already associated with the stream .

To get a valid dispatcher instance, you need to call Dispatcher.CurrentDispatcher from the user interface thread.

In addition, since the documentation says that it will automatically create a dispatcher if it does not exist for the current thread, this explains the silent failure. You get a dispatcher instance, but it is in no way associated with the user interface thread, so it does not actually send anything to the user interface thread.

(Removing this because in my tests I get null, even if I shouldn't, so it doesn't seem that much. The rest of the information is accurate, though) The documentation also added:

This does not apply to the FromThread method. FromThread will return null if the dispatcher is not associated with the specified thread.

So, to confirm that you are really getting an auto-created (invalid) dispatcher, try sending the dispatcher from Dispatcher.FromThread instead. I assume you get null .

If you want to call dispatcher.BeginInvoke to force the method to execute on the user interface thread from the workflow, you need to call Dispatcher.CurrentDispatcher from the user interface thread and save it in a variable. You can then pass this dispatcher reference variable to the workflow and call BeginInvoke .

 // capture and save dispatcher from UI thread Dispatcher dispatcher = Dispatcher.CurrentDispatcher; // then you can do this from your worker thread: dispatcher.BeginInvoke(theMethod); 

Alternatively, use this.BeginInvoke , as you already do.

Or even better, you can try using tasks in conjunction with the new async-await keywords for this kind of thing.

EDIT

For completeness, I must explain why Control.BeginInvoke working correctly.

The Control.BeginInvoke documentation says:

Runs the specified delegate asynchronously in the thread on which the control base descriptor was created.

And later he also adds:

You can call this method from any thread.

The fact is that when calling Control.BeginInvoke it does not use the current thread to determine how to execute the delegate. It remembers which thread the control was created (user interface thread), and ensures that the delegate runs on that thread.

So, while your control is being created in the user interface thread (as it should be), then BeginInvoke works from any thread. This is actually very similar to Dispatcher in that as long as you first get a Dispatcher instance from the UI thread, then you can call dispatcher.BeginInvoke from any stream.

+3
source

All Articles