onuserpreferencechanged hang - dealing with multiple forms and mutlipe ui threads - c#

I think my problem is similar to:
.NET 4.0 and the dreaded OnUserPreferenceChanged Hang
I have also looked through:
http://ikriv.com/en/prog/info/dotnet/MysteriousHang.html#BeginInvokeDance
I have removed our splash screen.
I also tried adding the suggested code: Microsoft.Win32.SystemEvents.UserPreferenceChanged += delegate { }; to our main() method.
I'm looking for some ideas and information on how to troubleshoot.
For our main() method we start a windowmanager class which is a form using Application.Run
It is just an icon in the task tray ( we don't show a window).
Whenever we launch an object we have a background thread which creates a form and then does Application.Run( form )
At the time of Application.Run( form ) form.IsHandleCreated = false.
I use the freezer application from the MysteriousHang website. (I modified it to keep sending the change notification in a loop ).
How should I be handling creating and running the new form? Does it matter that the form is created on the background thread even if its handle isn't created yet?

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.

Related

UI freezes when using SystemEvents.UserPreferenceChanged and multiple UI threads

In my C# Windows Forms application there are two threads:
Main Thread (Program.cs)
WorkerClass Thread (STA-Apartment).
When there is long running Task, it freeze/stuck the entire process and No exception or notification fired..it hangs application.
Internally applications doing processing of records only (selection from SQL tables & inserting in Access DB tables)
UI updates will be done using event Action feature.
Find attached snap for stuck process parallel tasks. Seems like threads internally wait for each other and get process blocked. Code related to the SystemEvents.UserPreferenceChanged event is on one of the stacks.
Why does this happen and how can I resolve it?
It deadlocks on a SystemEvents.UserPreferenceChanged event. This is the standard way an app with windows on more than one thread deadlocks. Best way to invoke the deadlock is to press the Windows+L key. You can see this deadlock analyzed in depth in this blog post.
The SystemEvents class is the troublemaker here, it makes an attempt to raise it events on the UI thread of a program. Which is very important, UI isn't thread-safe. Trouble is, you've got two threads that created UI. SystemEvents is incapable of guessing which one is right, it only has 50% odds at it so is doomed to get it wrong. If it initially guessed wrong at which thread in your program is the UI thread, and that thread exited, then it will be 100% wrong.
This makes creating UI on a worker thread exceedingly hazardous of course. It is technically possible, you'll however have to avoid using several controls from the toolbox. They don't handle the UserPreferenceChanged event well when it is raised on the wrong thread. The ones that definitely cause deadlock are DataGridView, NumericUpDown, DomainUpDown, ToolStrip+MenuStrip and the ToolStripItem derived classes. The ones that are iffy (can't analyze the code deep enough) are RichTextBox and ProgressBar. Looks like I ought to put ProgressBar in the first set, judging from your callstacks.
The true cure is to not create UI on a worker thread. It is never necessary, the UI thread of your program is already capable of handling any number of windows.

How will STAThread affect my multi-threaded program

I've recently encountered a STA-related error in my program when I tried to launch an OpenFileDialog in a WinForm. I've done some reading, and before I add the [STAThread] attribute to my main thread I want to know how it will affect my program's execution.
I am a foreigner to COM so not everything I read made sense to me. Some points that stuck with me are:
The [STAThread] attribute defines the application as using a single-threaded apartment model.
More specifically, it changes the state of the application thread to be single-threaded.
http://www.a2zdotnet.com/View.aspx?Id=93
The STA architecture can impose significant performance penalties when an object is accessed by many threads. Each thread's access to the object is serialized and so each thread must wait in line for its turn to have a go with the object.
http://www.codeproject.com/Articles/9190/Understanding-The-COM-Single-Threaded-Apartment-Pa
I understand the need for thread-safety but I still don't understand what STAThread does. In my program (which I inherited from another developer) the main thread launches several other threads, one of which initializes the UI forms - and I think this is where the problem arises. With [STAThread] added what happens to the new threads? Does this affect multi-thread communication for non-Windows objects?
The error occurs when I try to open an OpenFileDialog in one of my forms. I added the dialog to the form using the VS designer: it didn't work. I then attempted to create a dialog box in a global file which is run by the main thread and call that instance from my form. It had no effect.
[STAThread] or Thread.SetApartmentState() are a really, really big deal. You make a promise to the operating system that you write code that is well-behaved. It matters to lots and lots of code inside Windows as well as components you use that are not thread-safe. Standard examples of such code are the Clipboard, Drag + Drop, the shell dialogs (like OpenFileDialog), components like WebBrowser and many Windows sub-components that are wrapped by .NET classes.
Thread-safety is always a big deal, writing truly thread-safe code is very, very difficult. The .NET Framework itself accomplishes it very rarely. Very basic classes list List<> are not thread-safe.
By making the promise to behave well, you must abide by the rules of writing code in a thread that reports itself to be an STA thread. You must do two basic things:
You must pump a message loop. Aka Application.Run() in a Winforms or WPF app. A message loop is a basic mechanism by which you can get code to run on a specific thread. It is the universal solution to the producer-consumer problem. Which solves the thread-safety problem, if you call thread-unsafe code always from the same thread then it isn't unsafe anymore.
You must never block your thread. Blocking an STA thread is very likely to cause deadlock. Because it stops those chunks of code that are not thread-safe from being called. There is core support for this in the CLR, blocking an STA thread with WaitOne() causes it to pump a message loop itself.
These requirements are easily met in a Winforms or WPF app. They are class libraries that were completely designed to help you implement them. Almost every single aspect about the way they behave was affected by it.
You must mark the Main() method in a GUI app as [STAThread]. Rock-hard requirement when it creates windows.
Creating another thread that displays a window is supported and possible. This time you must call SetApartmentState() to switch to STA, it cannot be a thread-pool thread. Getting this right is very difficult, in Winforms you'll get bitten badly by the SystemEvents class if you use certain kind of controls. It has a knack to start raising its events on the wrong thread. Debugging such a problem requires black-belt skills that look like this. That's suppose to scare you.

Performing long running UI changes without blocking the main thread

Is there anyway to run a large number of UI updates without effecting the main thread in a C# winforms application?
I would like to avoid a long delay when a user clicks a specific event (which in my case is many close form calls to dispose of memory)
I know i can use BackgroundWorker to perform lengthy operations in the "do work" event, but the problem is that you cant change any UI in this event (it will cause a cross thread exception) - so i cant put my large number of close form calls here.
And I cant put the close form calls in the "worker completed" event because this is run on the main thread, and will eventually lockup the main thread causing a bad user experience.
I have thought about spawning a thread to handle closes only when the appication session is idle, but not sure if this is going to be a bit messy.
You should use ProgressChanged event of BackgroundWorker to update UI. To enable this feature set WorkerReportsProgress property of your BackgroundWorker instance to true. Then you can update UI many times by sending data from DoWork event handler:
backgroundWorker.ReportProgress(percentage, yourCustomData);
It is recommended to Make Thread-Safe Calls to Windows Forms Controls. Here is the reason:
Access to Windows Forms controls is not inherently thread safe. If you
have two or more threads manipulating the state of a control, it is
possible to force the control into an inconsistent state. Other
thread-related bugs are possible as well, including race conditions
and deadlocks. It is important to ensure that access to your controls
is done in a thread-safe way.
The .NET Framework helps you detect when you are accessing your
controls in a manner that is not thread safe. When you are running
your application in the debugger, and a thread other than the one
which created a control attempts to call that control, the debugger
raises an InvalidOperationException with the message, "Control control
name accessed from a thread other than the thread it was created on."
This exception occurs reliably during debugging and, under some
circumstances, at run time. You are strongly advised to fix this
problem when you see it.
You can disable that exception:
Form.CheckForIllegalCrossThreadCalls = false;
But controls could (and sometime will) stop working.

Invalid Cross thread Operations C#

I have a windows form with some controls on it. One of the controls is a textbox and the other one is listView. I also have a button (Upload) that uploads files based on the items selected item ListView object.
To report upload progress %, i added a progress bar that created a background worker thread that would upload the files, by contacting the server. The progress bar does not updates properly and appears unresponsive without the approach of creating background worker.
Now, while uploading files, i need to get the selection from ListView and get files based on that selection. But when i try to access "ListView" from background worker thread I get an exception: System.InvalidOperationException: Cross-thread operation not valid:
What should i do to correct this exception?
In this case your processing thread wants to access your UI thread.
Example:
private delegate void UpdateTextDelegate(object value);
private void UpdateText(object value)
{
if (this.textbox.InvokeRequired)
{
// This is a worker thread so delegate the task.
this.textbox.Invoke(new UpdateTextDelegate(this.UpdateText), value);
}
else
{
// This is the UI thread so perform the task.
this.textbox.Text = value.ToString();
}
}
Access to Windows Forms controls is not inherently thread safe. If you have two or more threads manipulating the state of a control, it is possible to force the control into an inconsistent state. Other thread-related bugs are possible as well, including race conditions and deadlocks. It is important to ensure that access to your controls is done in a thread-safe way.
The .NET Framework helps you detect when you are accessing your controls in a manner that is not thread safe. When you are running your application in the debugger, and a thread other than the one which created a control attempts to call that control, the debugger raises an InvalidOperationException with the message, "Control control name accessed from a thread other than the thread it was created on."
This exception occurs reliably during debugging and, under some circumstances, at run time. You are strongly advised to fix this problem when you see it. You might see this exception when you debug applications that you wrote with the .NET Framework prior to .NET Framework version 2.0
BackgroundWorker also uses a thread to do it's job.
Look at this
No code should touch any Control unless it's running on the UI thread. This means invoking methods (other than Control.Invoke()) and setting/retrieving properties. The best way to accomplish this is to make liberal use of Control.InvokeRequired and Control.Invoke(). For your particular case, you may want to look into using a BackgroundWorker to do your file uploading.
You cannot access controls created on one thread from another thread. This is a good thing, trust me. The BackgroundWorker class exposes a couple of events that can help you accomplish what you want, namely ProgressChanged and RunWorkerCompleted.
I wrote a detailed guide to some of the techniques to handle this on a forum I frequent. Look at the explanation under the "Classic Marshalling" header for a walkthrough of what the implementation looks like.
Usually, I use the ReportProgress method of the BackgroundWorker component to marshall elements from the background thread to the UI thread.
You can simply pass the progress (integer) value and you can as well pass complex objects (non-GUI only, so no ListViewItems or the like) to the GUI thread in this event.
The good solution would be using Control.Invoke/Control.InvokeRequired - it helps executing code in the GUI thread (thus not throwing an exception).
The bad/ugly solution would be Control.CheckForIllegalCrossThreadCalls = false.

Are there any problems with having a group of win forms in one thread and another group in another thread?

I have an application that has two distinct groups of win forms and I want each group to operate in separate threads. Are there any problems with this approach as long as I BeginInvoke/Invoke when operations happen across the different threads?
This question stems from the fact that I've always been used to thinking in terms of a 'gui thread' that I must if (InvokeRequired) { Invoke } else { ... } and all forms live on that thread.
An alternative angle on this question:
Is there anything 'special' about the default thread that win forms exist in or is it the same as any other thread?
Well, there are ways to shoot the foot but Windows Forms rarely forgets to tell you about it.
Yes, there's something special about the "main thread". It runs in STA mode, a Single Threaded Apartment. It is a mode that affects COM components, the shell dialogs like OpenFileDialog and operations like Drag + Drop and the Clipboard. Threads that display a UI always must be STA. That's automatic in normal WF apps with the [STAThread] attribute on the Main() method. In your own app you have to call Thread.SetApartmentState() before you start it. And the thread is special because it pumps a message loop (Application.Run), a requirement for STA threads.
By default, any Thread you start or any threadpool thread runs in MTA mode. Threadpool threads cannot be changed, they are always MTA.
It should work just fine, I am pretty sure that in my current project it is implemented this way and we didn't see any issues with this. You just need to remember to use the correct control when you use InvokeRequired and Invoke methods.
A GUI thread simply pumps messages so that it can process the standard Windows messages, I don't think there is anything else special about it.
the only problem that I can think of is related to very very old COM components, that are related to the main single threading apartment.
msdn.microsoft.com
but this is very unlikely

Categories

Resources