Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET 4.0 and the dreaded OnUserPreferenceChanged Hang

I have been plagued with the dreaded OnUserPreferenceChanged Hang that's refered to quite nicely by Ivan Krivyakov, here:

http://ikriv.com/en/prog/info/dotnet/MysteriousHang.html#BeginInvokeDance

I posted a question a while back, when I originally encountered the problem:

Yet another C# Deadlock Debugging Question

I thought I had solved it by removing a Control that was constructed off the UI thread, but after a little while it reappeared (probably never left...).

We've been using .NET 3.5, which I understand uses CLR 2.0. Recently, the applciation has been upgraded to use .NET 4.0 Client Profile / CLR 4.0. In addition, we've upgraded from Infragistics WinForms 10.1 to 10.3. The only other difference is that the previous version is obfuscated... has anyone experienced issues with obfuscation and hanging?

I've had another stab at getting rid of any application hangs once and for all, but unusually, I have not been able to reproduce the hang in the most recent version (using .NET 4.0). The hang is simple to reproduce in the previous version (using .NET 3.5), using Ivan Krivyakov's handy Freezer application (see his article for it), which fires a WM_SETTINGCHANGE message upon request.

It may be me being a little hopeful that the issue has disappeared off it's own accord, but does anyone know if there have been any changes to the CLR from 2.0 to 4.0 that would cause this?

-----------------------------------------------------SOLUTION--------------------------------------------------

So after testing variations of the application e.g. CLR 2.0 + Infragistics 2010.1, CLR 2.0 + Infragistics 2010.3 and CLR 4.0 + Infragistics 2010.1, we believe we've identified the problem to have been an issue with an Infragistics component in WinForms 2010.1 (no hot fixes). We still have yet to reproduce the freeze using either CLR 2.0 or CLR 4.0 with Infragistics 2010.3, instead (and we've gotten pretty good at reproducing this now...).

like image 223
Roo Avatar asked Nov 02 '10 12:11

Roo


2 Answers

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.

like image 136
Hans Passant Avatar answered Sep 28 '22 01:09

Hans Passant


The best guide I've found for resolving this issue is here:

  • Debugging Windows Forms Application Hangs During SystemEvents.UserPreferenceChanged - The DSUI Team Blog - Site Home - MSDN Blogs

It walks you through using WinDbg to verify the cause of the error and shows you how to find what's causing it. As you mentioned it is most likely caused be a control being created on a non ui thread.

In my case I resolved the issue by creating a factory that uses the SynchronizationContext from the UI thread to create the control and then I call CreateControl() in order to force the creation of a UI handle.

The Microsoft Support article is here:

  • Windows Forms application freezes when system settings are changed or the workstation is locked
like image 24
aboy021 Avatar answered Sep 28 '22 01:09

aboy021