Getting multiple UI threads in Windows Forms - c#

I'm trying to figure out a way to make user controls run in their own UI threads. Is this possible? I'm trying to prevent a module-based application from crashing due to a single module.
Any thoughts?

That's not possible. However, with some non-trivial code, you can have different windows running in separate threads. Each window will have its own message loop.
Update:
Another way you could think of is to write your controls in a special way. You can handle all events in your controls by creating a new thread that will run all the logic.

Unfortunately all UI controls run on the same UI thread. Therefore any code running on this thread that could potentially lead to a hang situation would need to be coded with some sort of timeout logic.
DateTime startTime = DateTime.Now;
while(DateTime.Now.Subtract(startTime).TotalSeconds < 30)
{
//do something
}
Otherwise, as Orlangur stated earlier, all event handler code would need to be run in separate threads. You would still however need to monitor these threads to determine if they've been running too long and shut them down. As such you might as well implement the type of logic above as it would be a lot less work and more maintainable.

I suppose it's not a matter of the program crashing. Exceptions can be caught of course, but the issue is in hanging controls. For the sake of this situation, here's an example:
public void Button1_Click(object sender, EventArgs args)
{
while(true) {}
}
If this code were to run in a control, an exception wouldn't throw, but it would hang. I'm trying to determine a way to catch this and remove the control module from the application.

Running controls in different threads should be possible. A little hacking and windows overrides and it should be doable.
I am thinking you can create a GUI control in another thread, then move it to a common window (main gui thread) with the win api SetParent. SetParent can be used to "hijack" other windows, so you should be able to grab the controls this way. But of course there might be focus issues and other issues, but might be doable.
I used that once to put my own button onto MS Messenger.

Related

C#: Parallel forms, multithreading and "applications in application"

First, what I need is - n WebBrowser-s, each in its own window doing its own job. The user should be able to see them all, or just one of them (or none), and to execute commands on each one. There is a main form, without a browser, this one contains control panel for my application.
The key feautre is, each browser logs on to secured web page and it needs to stay logged in as long as possible. Well, I've done it, but I'm afraid something is wrong with my approach.
The question is:
Is code below valid, or rather a nasty hack which can cause problems:
internal class SessionList : List<Session> {
public SessionList(Server main) {
MyRecords.ForEach(record => {
var st = new System.Threading.Thread((data) => {
var s = new Session(main, data as MyRecord);
this.Add(s);
Application.Run(s);
Application.ExitThread();
});
st.SetApartmentState(System.Threading.ApartmentState.STA);
st.Start(record);
});
}
// some other uninteresting methods here...
}
What's going on here? Session inherits from Form, so it creates a form, puts WebBrowser into it, and has methods to operate on websites. WebBrowser requires to be run in STA thread, so we provide one for each browser. The most interesting part of it is Application.Run(s). It makes the newly created forms alive and interactive. The next Application.ExitThread() is called after browser window is closed and its controls disposed. Main application stays alive to perform the rest of the cleanup job.
When user select "Exit" or "Shutdown" option - first the browser threads are ended, so Application.ExitThread() is called. It all works, but everywhere I can read about "main GUI thread" - and here - I've created many GUI threads. I handle communication between main form and my new forms (sessions) with thread-safe methods using Invoke(). It all works, so is it right or is it wrong?
Is everything right with using Application.Run() more than once in one application? :) An ugly hack or a normal practice? This code dies if I start a WebBrowser from the session form thread. It beats me why. It works however if I start WebBrowser (by changing its Url property) from any other thread. I'd like to know more what is really happening in such application. But most of all - I'd like to know if my idea of "applications in application" is OK.
I'm not sure what exactly does Application.Run() do. Without it forms created in new threads were dead unresponsive. How is it possible I can call Application.Run() many times? It seems to do exactly what it should, but it seems a little undocumented feature to me. I'm almost sure, that the crashes are caused by WebBrowser component itself (since it's not completely "managed" and "native"). But maybe it's something else.
Is everything right with using Application.Run() more than once in one application? :) An ugly hack or a normal practice?
Some of both ;) This is perfectly acceptable, in that it will function as you are expecting, but it's not exactly a "normal practice."
I'm not sure what exactly does Application.Run() do.
Application.Run basically does a couple of things. First, it installs the property SynchronizationContext into the thread for Windows Forms to run properly. It then starts the windows message processing in that thread, which processes all messages from Windows going into the thread. This is what allows forms to work properly.
There's nothing particularly wrong with doing this, but it isn't a standard practice. Given your design goals, I do question whether this would be better served by just launching a separate process instead of trying to run each operation within a separate thread.

Open new form from inside each thread?

I'm using the following code to open a new form (that is for user input) in my function:
Form4 f4 = new Form4();
f4.mainform = this;
f4.get_Text(matchString);
f4.ShowDialog();
It's working perfectly when not threading, but when I try to run the function on two threads I only get 1 form popup. How can I adjust it so that it will support more than one thread?
You can open a form from any thread, you just need to start a message loop on the new thread, such as:
Application.Run(f4)
The Run method will not return until the form is closed. It can cause issues, though, so you need to be careful and know what you are doing. Unless it's a real pain to do, I would sooner consider redesigning your code to be more asynchronous. For instance, you could create a event handler method to listen for the form's closing event.
The WIN32 Message pump runs on one thread -- the GUI thread. As soon as you .ShowDialog(), the c# stops until the user closes the dialog. Even if you use .Show() to avoid a "stop and wait for the user to push ok", you still must handle all GUI events on the GUI thread by .Invoke()ing to the GUI thread.
The way to support multi-threaded-ness is by doing non-GUI work on alternate threads.
ShowDialog does pump messages so it would technically work on a separate thread without needing a dedicated message loop. However, what you currently have looks like a recipe for disaster because that form appears to hold a reference to another form via f4.mainform = this and it is presumably trying to access it. You simply cannot do this without littering (and I mean that literally) your code with a bunch of Invoke or BeginInvoke calls.
Furthermore, running UI forms on a thread other than the main UI thread generally does not work well. There are a few obscure problems you can run into. For example, since there would be two UI threads in play it is possible to have 2 active modal dialog boxes open. One might be hidden behind the other and the end user would not see it. This reason, among others, is why it is not generally recommended to use more than one UI thread.
My advice is to figure out a way to make this work with a single UI thread. Your life will be simplier if you do.

Is there an 'Application.DoEvents' at the form level?

I currently have an application that spawns multiple instances of a single win form. Each instance of the form has a timer which updates a number of controls and some of the properties of the form itself at rate of approx 1 tick/sec. Once the application reaches a certain number of forms it stops painting the 'updates' that occur within the timer ticks.
I want to hold the UI thread at the bottom of the timer and allow the form to repaint without calling Application.DoEvents (my understanding is that Application.DoEvents() handles all pending window messages across the application (which could include other timers 'ticks'? - not sure) when all I want it to process is the redrawing of the form now that a number of the controls have been updated).
It's not clear to me why you want to call DoEvents at all. Why not just let the window repaint itself appropriately? What's wrong with the way it behaves at the moment?
Calls to DoEvents usually indicate that something should be in a background thread - in your case it sounds like it might actually just be that you've got too many events going on. If your GUI is so complex that it can't redraw itself in a second, it sounds like that's probably a design issue with the UI itself.
If you just want the form or a specific control to redraw itself, just call Control.Refresh on the form/control in question.
That being said, this is usually (like Application.DoEvents) a sign that a redesign is in order. If you have long running processes like this, it's a much better idea to push the work into a BackgroundWorker, which will make the actual computational work run in a background thread, and prevent the UI from becoming unresponsive.
There are three categories of Windows messages that are dispatched by the message loop and a call to Application.DoEvents(). First are messages that are sent with SendMessage(). They are important, they need to be dispatched right away because there is some other program waiting on the result of the SendMessage() call. They are not put on the message queue, Windows calls the window procedure directly.
Then there are messages that are put on the message queue with PostMessage(). They can wait, they are merely notifications. All of the keyboard and mouse messages fit that category.
Then there are the low priority messages, WM_TIMER and WM_PAINT. They only get dispatched when there's nothing else to do, no SendMessage is pending and the message queue is empty.
Sounds like you got yourself into a situation where you starving Windows so badly that it can't get around to dispatching those low priority message anymore. Probably neither getting timer Ticks nor Paint events anymore. Calling DoEvents only once a second will certainly do that. This is bad, your program is now affecting the operation of other programs as well. You'll have to fix that. Do so by re-architecting your app so you no longer depend on DoEvents.
My 2c:
Having a timer in each form is going to slow down performance to a crawl. Each window at each second interval is going to request the application to switch to that window's "context" and execute some code. That whole switch is likely longer than your code actually takes to run. Couple that with the likely fact that all of your forms are in the same "context".
Instead consider something like this if possible:
List<MyForm> listofmyform;
Timer t = new Timer()
t.tick += delegate
{
foreach(MyForm thisform in listofmyform)
thisform.DoUpdate();
}
This only uses one timer to update all of your forms, only requires one "context switch" and will likely cure some performance problems you're having.

Is there a way for one .NET Control to contain another Control which is owned by a seperate GUI thread?

I'm looking at creating a tabbed interface which has user controls (possibly written by plug-in developers) within a tabbed or MDI interface. These plug-in controls could unintentionally freeze their GUI thread, and I'd prefer that they not influence user controls in other tabs. Much like Google Chrome creates a process for each tab; but in this case, just threads.
Or perhaps even an MDI interface where the child MDI forms are owned by separate threads?
I've found that while I can run multiple GUI threads at once, the Form level is where they MUST be separated. Any workarounds/ideas?
For those saying this shouldn't be needed, I call bullshit. Google's Chrome browser runs tabs in separate processes for security and UI reasons. I'm merely trying to duplicate this behavior. When the people writing the user controls are sucky plug-in developers, this is important.
No it is not possible to do this in the way you are describing. A control which is owned / affinitized to another GUI thread cannot be directly contained within a control which is owned / affinitized to a different thread in such a way that it's paint function runs on the other thread.
The right way to fix this situation is to write UserControls that don't perform long-running tasks on the UI thread. If the control is blocking and waiting on some computational task, fix that. Make that task run in the background, and have the control display some non-compute-intensive content until it's done. If that task freezes, the control will be frozen in its "I'm waiting..." state, but it won't intrude on the rest of the UI.
If you're using a third-party control that you can't fix, well, in the immortal words of Jay-Z, I feel bad for you, son.
For the most part, controls shouldn't be performing any processing. Their purpose is to provide interactivity between the user and the application. For example, it is not the job of a button to fetch data from a database and present it to the user. That being said, hopefully you are doing your processing in a controls event handler, such as the Click event on the Button control. In your event handler, you can prevent the UI from appearing "hung" by processing tasks in a background thread. The BackgroundWorker is often useful in these situations.
I suggest reading up on Threading. The Microsoft® .NET Framework Application Development Foundation book has a section on threading (even if no other certification books are read, I at least recommend all .NET developers read this book). Just remember not to update the UI from a child thread. Read an example on how to make a thread-safe call to Windows controls if you're not familiar with this approach.
Instead of having or owning different GUI threads, you should view the whole issue from a different angle. Why would you want a thread associated to tab's child control to be freezed? If it does freeze and everything else feezes too, threading aside, that's not done right from ground up.
What JaredPar pointed out is correct, but that doesn't mean you cannot achieve what you want. I assume you want stuff running within a tab to continue running/stopping without affecting other controls and user-experience.
I've done it before in a complex WinForm app. Here are some readings which might give you more insights:
Threading out tasks in a C#.NET GUI
Thread and GUI
Updating GUI from Other Threads in C#
Advanced Techniques To Avoid And Detect Deadlocks In .NET Apps

C# Winforms How to update toolStrip in function

I'm building a UI for a program, and I can't figure out why my progress bar won't become visible after the convert button is clicked.
private void convertButton_Click(object sender, EventArgs e)
{
toolStripProgressBar.Visible = true;
...
toolStripProgressBar.Visible = false;
}
I ran into a similar problem with tkinter in Python, and I had to call a function to update the idle tasks. Is there a way to do this with windows forms without using threads?
Edit: On a side note, this is a progress bar in a toolStrip that also contains a label that gets updated with status bar text. Is there any way to get the label on the left side and the progress bar on the other instead of right next to each other on the left?
Well, there is a way to do this without using threads (Application.DoEvents) but I strongly recommend against you using it. Re-entrancy is nasty, and you really don't want the UI thread tied up at all.
Use BackgroundWorker instead - it's easy, and it's pretty much designed for progress bars. It takes the hassle out of using a separate thread and reporting progress back to the UI thread. No need for Control.Invoke etc - it takes care of that for you.
There are lots of tutorials for BackgroundWorker - it shouldn't take you too long to get going with it.
Per the question you asked for the way to do this WITHOUT threads, that is to do it with Application.DoEvents();. (Just add that call right after setting the progress bar as visible.)
Now I do agree with Jon Skeet though that BackgroundWorker is a better way of doing this, but it does use a separate thread.
You need to execute your process in a thread separate from the UI thread, and then have it periodically report back to the UI thread with it's progress. If your convert operation is working inside the UI thread, it will simply go unresponsive until the operation is complete.
The progress bar can only become visible when it is allowed to paint which occurs during the processing of messages. Message processing cannot normally happen while you are in the middle of an event handler. If you want the progress bar to show up you will have to set the visiblitity to true, start a background thread to complete the work and return from the handler.
I'm guessing the problem is that the "..." in your code is a long-running process. UI updates are not instantaneous, but must run through the message queue in windows and then be painted to the screen. The queue is pumped and painting takes place in the same thread as your events.
As a result, any long-running tasks need to be moved to a different thread. More than that, your line line of code needs to called after that thread terminates. Otherwise you set the progress bar and then immediately turn it off again.
One way to do that is with a BackgroundWorker control.
Here go two links trying to explain you how things work:
(1) (2)
Now, I will try to explain it as shortly as I can. Most of what happens inside a windows forms application happens in a single thread, usually the same thread Main() runs in. If you open Program.cs, you will see that Main() has a line that looks like the following:
Application.Run(new Form1());
If you debug the application at any moment and examine the call stack, you will see it will trace back to that Run method. This means that a Windows Forms application is in fact a continuous run of the Run method. So, what is Run doing? Run is eating a message queue through which Windows sends messages to it. Run then dispatches those messages to the correct controls, which themselves do things like add text which corresponds to the key being pressed, redraw themselves, etc. Notice that all this happens during and endless loop running alongside a single thread, so weather you are typing or simply moving the window around, loads of those messages are being passed onto the application, which in turn is processing them and reacting accordingly, all in that single thread. Controls can also send messages to themselves through the queue and even you can place messages in the pump via Control.BeginInvoke. One of the things those controls do is to raise events according to what happens. So, if you click a button, the code you've written to handle that click will ultimately and indirectly be run by the Application.Run method.
Now, what is happening with your code is that even though you are changing the visible status of your progress bar to visible and then updating its Value, you are then changing its visibility to false, all in the same method. This means that only after you leave the method, will Application.Run() be able to continue iterating and consuming the message queue, effectively asking the progress bar to update its display. When that happens, you've already left the progress bar's visibility to false, the last thing you did before exiting the method. DoEvents() is a quick and dirty workaround to your problem as it reads the messages in the queue and processes them. I don't really feel comfortable using it as it can bring reentrancy problems.
Using threads is a good solution, but I would recommend using a ThreadPool thread instead of a custom thread in this kind of situation, as I tend to use custom threads only in cases where I have a limited number of long lived threads and I need to control their life cycles. The easiest and most practical way to use threads is to use the BackgroundWorker component, even though I would recommend going through the pains of understanding how to do Windows Forms multithreading with delegates if you want to really understand what is going on.
My solution is to call refresh on the status strip.
I believe this causes the UI thread to repaint the status strip.
toolStripStatusBar1.PerformStep();
statusStrip1.Refresh();
This is for .NET 4.0. Even though this question is old it was the first I found on googling this issue.

Categories

Resources