C# UI – Cross Threading exceptions

This WPF form invites you to click a button, at which point text appears in the box:

WpfAsync01

WpfAsync02

Press [Button]…

WpfAsync03

Fine. (Sorry, I see I left the redundant Delegate reference in the screenshot above –  please consider it removed – it is correct in the PasteBin.)

If we now make the call to the edit asynchronous, then in debug mode in Visual Studio, we get a nice big exception:

WpfAsync04

WpfAsync05

{“The calling thread cannot access this object because a different thread owns it.”}

So a cross-thread exception because the worker thread is trying to access controls which belong to the UI/thread. Good. PasteBin. Having above run it as Debug/F5, I then ran it NoDebug/Ctrl F5, naively thinking it would somehow (?!) still expose the expose the exception at runtime. Well, an additional screenshot will add nothing, because when you click the button, the input screen just sits there, happily accepting the key presses, and on the face of it, doing nothing about it. I need to add some code that flags the exception somewhere. You might have thought that a .Net application runtime error might flag an exception to the event log… but again, why would it? If it did that every time on your average Windows box, I suspect you would soon run out of disk space. So you need to explicitly write to the event log, assuming you decide that is where you want your exceptions to be written. If this was a proper application, then you might create a dedicated event log type… but I’m just going to use Application. The code changes to use System.Diagnostics:

WpfAsync06

PasteBin

WpfAsync07

That is all good: the exception is persisted to the Event Log, but we now have failing code. The solution is to ensure that we only try to update the control when it is on the main/UI thread, and not on the worker thread. This is done in WPF by testing for (control.)Dispatcher.CheckAccess(). (Forms is certainly different, and Universal Apps and Silverlight might be different.) If it is true, then we are on the UI thread. If it is false, then we instantiate a new version of the delegate and call it on the UI thread via the Dispatcher, recursively calling back into the delegate implementation. WHA??!! I think your best bet is to set a breakpoint as I have shown here, and step into (F11) to understand what sequence it follows. You might notice that on subsequent executions, it is already on the UI thread. That feels weird, so maybe I did something wrong. I don’t think it matters, as the code works. And if you exit the app and start again, you’ll see it starts off again on the worker thread.

The result:

WpfAsync08

, and the code:

WpfAsync09

There is nothing in here that justifies the use of asynchronous behaviour. You will just have to take my word that when you do have a use-case, this will help.

Advertisements