.Net 4.0 and the Dreaded Onuserpreferencechanged Hang

onuserpreferencechanged hang - dealing with multiple forms and mutlipe ui threads

I'm also confused about the terminology "UI thread".

A UI thread is a thread that pumps a message loop. And operates in a mode that's compatible with user interface objects, it needs to be an STA, a Single Threaded Apartment. That's a COM implementation detail that matters a great deal to common UI operations that are not thread-safe and require an STA, like Drag+Drop, the Clipboard, shell dialogs like OpenFileDialog and ActiveX components.

It is the CLR's job to call CoInitializeEx() and select the apartment type. It does so guided by the [STAThread] attribute on the Main() entrypoint in your program. Present in projects that create UI objects like a Winforms or WPF app. But not a console mode app or service. For a worker thread, in other words a thread that was created by your code instead of Windows, the apartment type is selected by what you passed to Thread.SetApartmentState() method. The default is MTA, the wrong flavor. A threadpool thread is always MTA, that cannot be changed.

The SystemEvents class has the unenviable task of figuring out which thread is the UI thread in your program. Important so it can raise events on the correct thread. It does so by using a heuristic, the first thread that subscribes an event and is an STA thread is considered suitable.

Things go wrong when that guess wasn't accurate. Or certainly in your case where you try to create multiple threads that create UI objects, the guess can only ever be correct for one of them. You probably also forgot to call Thread.SetApartmentState() so it won't be correct for any of them. WPF more strongly asserts this and will generate an exception when the thread isn't STA.

The UserPreferenceChanged event is a trouble-maker, it is subscribed by some of the controls you find on the toolbox. They use it to know that the active visual style theme was changed so they'll repaint themselves, using the new theme colors. A significant flaw in the event handlers in some of these controls is that they assume that the event is raised on the correct thread, the same thread that created the control object.

This will not be the case in your program. The outcome tends to be unpleasant, subtle painting problems are a minor flaw, deadlock is certainly possible. For some reason, locking the work station with Windows+L and unlocking it is particularly prone to causing deadlock. The UserPreferenceChanged event is raised in that case because of the desktop switch from the secure desktop the user's desktop.

The controls that listen to the UserPreferenceChanged event and do not use safe threading practices (using Control.BeginInvoke) are DataGridView, NumericUpDown, DomainUpDown, ToolStrip+MenuStrip and the ToolStripItem derived classes, possibly RichTextBox and ProgressBar (unclear).

The message ought to be clear, you are using unsafe threading practices and they can byte. There in general is never any point to creating UI on a worker thread, the main thread of a Winforms or WPF program is already quite capable of supporting multiple windows. Short from avoiding the dangerous controls, this is what you should strive for to get rid of the problem.

What causes SystemEvents.UserPreferenceChanged to be invoked?

According to this MSDN Link. UserPreferenceChanged Event may occur when one of the Events in the below categories are triggered!. This is for a class of type UserPreferenceChangedEventArgs. I think the Description is self explanatory and clear.

Sample Image

C# application keeps freezing on remote

I do not think the freeze has anything to do with remote desktop. Adding logging was a good suggestion. I have a few suggestions, but without knowing the details of you application I can't get too specific.

The simplest suggestion I have is to check the memory useage and CPU usage in task manager when the freeze occurs.

If adding detailed logging is not an option, add just enough logging to know WHEN the application freezes. This could simply be a thread in the application which writes a timestamp to a file once a minute. Then you can see if there is any pattern in when it freezes, such as after a user has logged off, or when some of the data which you are monitoring changes, or at a certain time each day, or after being online for a certain amount of time.

A final and very hacky solution is to write a small watch dog application. This application's only job is to periodically check on the main application to make sure it is still responsive. How you dow this various drastically based on what your application does. If the watchdog sees the the main application has stopped, it can kill the thread for the main application and re-start it from the binaries.



Related Topics



Leave a reply



Submit