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

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.

Related

Creating a Form and using Form.ShowDialog on a background thread

Using Winforms,
If I'm on a thread that is not the "main" ui thread can I safetly
Create a Form,
Call ShowDialog on that form
It seems like I would be obeying the rule of:
Winforms controls must be modified on the thread they were created in.
From some of the reading I've done it seems like ShowDialog will create its own message pump so that I don't need to worry about the Form being created on a thread that already has a message pump.
The background of the question is that I get a callback where I'd like to display some Winforms UI, but I don't have access to any other UI that I could use to Invoke to the main thread.
That's roughly correct, albeit that it is pretty important that you call the thread's SetApartmentState() method to switch the thread to STA. Important for many UI operations, including the clipboard, drag and drop and the shell dialogs. And that you usually have a crummy Z-order problem when the form that you create on the thread is not in the foreground and hides behind another window. Or has the nasty habit of actually do move in the foreground when the user doesn't expect it and grab a mouse click or keystroke unexpectedly.
These are hard problems to fix, they do make your app flaky. There's no good reason to not have a reference to invoke to, you can also pass it to the class some way some how. You've always got Application.OpenForms[0] to fall back on, if really necessary.
Yes, you can do that, but if you want the dialog to actually act like a modal dialog (i.e., block the parent Window, which I assume you want since you are calling ShowDialog) then be prepared to be disappointed.
What problem are you actually trying to solve here. It sounds like you want a modal dialog that doesn't block, which is a bit strange. If you explain the problem at hand there may exist a solution you have not yet considered.

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.

Open InternetExplorer COM object to do some automation using Wating from WPF app, but keep UI responsive. is this possible?

I have an app that runs a process which needs to open an internet explorer, go to an url, and do some stuff there, input some data, and return.
This usually takes a while (several pages that need to be filled, doesn't matter). The problem is, while this process is being done, the calling app (a standard wpf app) UI is unresponsive/frozen. When the process returns, i have some info that i need to set in one of the WPF app's objects
The main problem is that since IE needs to be called from within an STA thread, if I try to call it from within the dispatcher asynchronously or synchronously, for some reason the UI is blocked (i have tried with different DispatcherPriority-es but didn't get lucky).
If i start a new BackgroundWorker, that thread is in MTA mode (and i can't switch it back), so there's a problem and i have an exception
I'm really lost here, could anyone put some light into this? maybe what i want to do is simply not possible.
Have you tried creating your own background thread (not relying on BackgroundWorker), and set its ApartmentState to STA?

Does MessageBox.Show() automatically marshall to the UI Thread?

I launch a thread via ThreadPool.QueueUserWorkItem which has a messagebox dialogue in it:
System.Windows.Forms.DialogResult dr = System.Windows.Forms.MessageBox.Show("would you like to download upgrade in background? ..", "Upgrade Available", MessageBoxButtons.YesNo);
It seems to work fine however I am a little suspicious after some customers suggested they were not getting the message popping up. I had the feeling in .NET framework 2.0+ you do not need to marshal this particular call, it does it for you. Correct?
This is a semi-related topic for interest:
Why use a owner window in MessageBox.Show?
No, it doesn't Marshal to the UI thread. If you think about it, it wouldn't be possible for it to do so.
It's possible to have more than one UI thread in an application. Some programs, such as internet explorer, have many UI threads. Which UI thread would the .Show call pick?
It's also possible to use MessageBox.Show in an application that has no UI threads. You can very well call this in a Console application and it will work.
MessageBox.Show will show UI on the thread it is called from. If there isn't already a message pump running on the thread it will setup a temporary one in order to function. It will tear it down after the Show call completes.
As a general rule, you shouldn't do GUI work outside of the main/application thread. I'd make a ShowMessageBox method on the parent form that can do an Invoke:
public DialogResult ShowMessageBox (String message, String caption)
{
if (this.InvokeRequired) {
return (DialogResult) this.Invoke (new PassStringStringReturnDialogResultDelegate (ShowMessageBox), message, caption);
}
return MessageBox.Show (this, message, caption);
}
public delegate DialogResult PassStringStringReturnDialogResultDelegate (String s1, String s2);
BUT ALSO KEEP IN MIND: when debugging a multi-threaded GUI app, and you're debugging in a thread other than the main/application thread, YOU NEED TO TURN OFF the "Enable property evaluation and other implicit function calls" option, or else VS will automatically fetch the values of local/global GUI objects FROM THE CURRENT THREAD, which will cause your application to crash/fail in strange ways. Go to Tools->Options->Debugging to turn that setting off.
Sorry for the caps, but this took me DAYS to figure out why I every time I tried debugging my app it would hang and then crash.
Well, I would marshal and specify a window, if only so the MessageBox can get the correct focus. It might be they simply can't see it because it is behind one of your forms, and doesn't know it should be in the foreground.
Is this an application or a service. If it's a service, maybe it's not set up with the 'Allow interaction with desktop' permission.
See the properties of the service in the services control panel applet.

Getting multiple UI threads in Windows Forms

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.

Categories

Resources