.NET 4.0 and the dreaded OnUserPreferenceChanged Hang

a Control that was constructed off the UI thread…

Yes, that is a good way to trigger this problem. The underlying problem is caused by the SystemEvents class, it has the unenviable task of raising its events on the correct thread. The UserPreferenceChanged event is the typical trouble-maker, lots of controls subscribe it so they can repaint themselves when the user changes the desktop theme. A component vendor would not overlook the need for this. Nor do the standard .NET framework controls in the toolbox.

A usually decent way to test for this problem is to lock the workstation (press the Win+L keys), also the way the deadlock is commonly triggered on the user’s machine. The switch to the secure desktop tends to trigger the event. With the additional quirks that this never happens when you debug your program and that it has tricky time-related behavior since this tends to happen when nobody is at the machine. Extra hard to debug.

One standard way to get into trouble like this is because of an initialization problem in the program. The very first SystemEvents event that is subscribed causes the SystemEvents class to initialize itself and setup the plumbing that’s necessary to receive these notifications and raise their corresponding event. A custom splash screen that does too much (i.e. more than just display a bitmap) and runs on a worker thread that is marked as STA is enough to get this wrong. Something as simple as a ProgressBar is already enough. SystemEvents assumes that the worker thread is the main thread of the program and can now easily generate the events on the wrong thread in the future. There is one good diagnostic for this, if that worker thread is no longer around then that generates a first-chance exception. You can see it in the Output window.

Or you create another UI thread and have forms on both threads. Inevitably one of these forms is always going to get the event on the wrong thread.

The only decent advice is to acknowledge that creating UI on a worker thread is rocket science that Microsoft did not know how to do correctly either. Notable is that the .NET 1.x controls have an event handler that still works correctly when it is called from the wrong thread, it merely call Control.Invalidate(). But that was knowledge that appears to have been lost at 2.0, ToolStrip is a good example. And do not trust a component vendor to get this right, Infragistics in particular does not have a stellar reputation. Don’t do it.

Leave a Comment