ApplicationContext and the UI thread
The App
I had this very annoying threading issue which took me ages to finally find the answer to in a dark corner of the .NET API. I have this Windows Forms application which sits in the system tray working away, and you can double-click the icon to open a window showing the application details.
One part of the application asynchronously spawns a child process and the "tray" class receives events, such as process started, died, output received, etc. On these events, the tray's tooltip text is updated (also a balloon tip may be shown) and also changes are made to the available right-click context menu items.
So, to create the tray icon without creating a form, I used a custom ApplicationContext instance as described Mitchel Sellers here. This worked fine, however after a seemingly random length of time, the application would freeze. The right-click menu wouldn't show and if the main form was open, it wouldn't respond or repaint.
UI Thread Deadlock
This looked like a classic UI threading issue. In normal WinForms programs, all interactions with a UI element (textbox/label/etc) have to be made on the UI thread. However, in the case of my tray app, this is all happening within the ApplicationContext class, which doesn't have the usual InvokeRequired/BeginInvoke methods. So I was calling the InvokeRequired/BeginInvoke methods on the tray's context menu I'd created within the ApplicationContext class.
These callbacks were all doing the InvokeRequired check, so I spent a good while on a wild goose chase looking at other areas of the application where I was locking, or should be locking fearing a deadlock was happening somewhere else which was causing the freeze.
Then I decided to add copious amounts of logging to try and find the area of code just before the freeze. At this point it became immediately obvious. From the logging, I could see that the callbacks weren't being marshalled back to the main UI thread - the thread that they were created on. During my search for the answer, other people solved this by using the MainForm property of the ApplicationContext instance - however, in my case there isn't a main form instance, so I couldn't use this.
SynchronizationContext to the Rescue
I finally found one random forum with a user posting the same problem and answered by himself with:
I found my solution, all I needed was SynchronizationContext.
Thank You! So I go in search of said class and it turns out to be very easy to fix my app:
private readonly SynchronizationContext syncContext;
public MyApplicationContext() {
syncContext = new WindowsFormsSynchronizationContext();
}
private void BeginInvoke(Delegate callback, object[] args) {
syncContext.Post(state => callback.DynamicInvoke((object[])state), args);
}
Problem solved. Instead of using the context menu control to marshal onto the UI thread, which doesn't work in the case, switch the code to use this new BeginInvoke method using the SynchronizationContext. Finally no more app hangs.
