creating new windows in a threadsafe manner - c#

I am using signalR to push messages up to server and down to specific clients.
When I send a message, the receiving client is sent a ReceiveMessage function call from my signalR server, which maps to a static class function in my app. The static class then tries to do new Chat() (my messaging window class) to open a message window on the receiving side.
This throws calling thread must be STA, because many UI components require this.
I have worked with delegates before on simple form elements (like changing a textbox), but I don't understand how to apply that in this situation. When I read about Invoke, it is called on a form object of some sort, which does not exist in my situation?
TLDR; how do I create and show a new instance of a form from a static class function call

Use the current GUI's thread dispatcher. Then call invoke. Par example:
Application.Current.Dispatcher.Invoke(() => {
var win = new Window();
win.show();
});

Easiest: make use of App.MainWindow.Dispatcher to perform the create on the GUI thread:
App.MainWindow.Dispatcher.BeginInvoke((Action)(() => { new Chat(); }));
Harder: create a new STA thread, and create the new Window on it.
The latter option is harder because you'll have multiple GUI threads and need to ensure you keep track of operations so the right thread is used in each case. Unless you have a specific reason to have multiple GUI threads stick with the easy option.

You have to invoke your code in the UI thread. To do this you need a reference to the syncronization context.
Suppose you invoked the following code from UI thread:
SynchronizationContext syncContext = SynchronizationContext.Current;
Hereby you got a reference to the context.
Then in the callback method(after receiving a message in the background thread(not UI)) you can do the following:
syncContext.Post((state) =>
{
Window w = new Window();
}, ...);
The code in lambda expression is executed on behalf of UI thread.
As far as I know this is what actually happens behind the scene in both "Control.Invoke" and "Dispatcher.Invoked" approaches.

Related

Windows Client GUI Design Advice - Accessing UI Thread from Long Running Task

Web Developer here and need some advice on how to achieve what must be a common requirement in Windows Forms.
I have a windows client app that calls a business object in a separate project to perform some long running tasks. Difference to other examples is that the process live in another class library i.e. Business.LongRunningTask();
I have a list box in the client that I would like to have logged to by the task. I can run the process on the UI thread passsing in the instance of the textbox and calling Application.DoEvents() when I log to the textbox from within the task. All fine, but not elegant and would prefer not to call Application.DoEvents();
If I run the long running process on a separate thread using delegates I cannot access the textbox or delegates created in the windows client form which rules out BeginInvoke calls.
Surely this is bad design on my part and would appreciate some feedback.
You're looking for the BackgroundWorker class.
To execute a time-consuming operation in the background, create a BackgroundWorker and listen for events that report the progress of your operation and signal when your operation is finished.
You can find a complete example here: http://msdn.microsoft.com/en-us/library/b2zk6580(v=VS.100).aspx#Y1351
I can run the process on the UI thread
passsing in the instance of the
textbox and calling
Application.DoEvents() when I log to
the textbox from within the task.
Yes, you could also pass in an instance of ILoggingINnterface that you have used to put in the code to write to the text box FROM WITHIN THE UI and thus have taken care of all the nice BginInvoke stuff ;)
If I run the long running process on a
separate thread using delegates I
cannot access the textbox or delegates
created in the windows client form
which rules out BeginInvoke calls.
Ah. No. You just most invoke back to the dispatcher thread then you can access all the UI elemente you like.
Yeah, avoid Application.DoEvents().
To marshall the call back onto the UI thread, call this.Invoke(YourDelegate)
To access UI elements from a different thread, you can use control.Invoke to call a delegate on the owning thread.
I used this at one point to create a live log screen which was updated from a timer while a different worker thread was running. Heres a simplified version:
public class DifferentClassLibrary
{
public delegate void StringDataDelegate(string data);
public event StringDataDelegate UpdatedData;
public void DoStuff()
{
if (UpdatedData != null)
{
Thread.Sleep(10000);
UpdatedData("data");
}
}
}
And in the winform:
public void UpdateTextBoxCallback(string data)
{
if (uiTextBoxLiveLogView.InvokeRequired)
{
uiTextBoxLiveLogView.Invoke(new DifferentClassLibrary.StringDataDelegate(UpdateTextBoxCallback), data);
}
else
{
uiTextBoxLiveLogView.Text += data;
}
}
void Main()
{
DifferentClassLibrary test = new DifferentClassLibrary();
test.UpdatedData += UpdateTextBoxCallback;
Thread thread = new Thread(new ThreadStart(test.DoStuff));
thread.Start();
}

DirectShow/WPF Threading issue

I am writing an app using WPF and DirectShow and have run into a sticky issue. My application utilizes DS through static methods Start() and Stop() in a static class written using DirectShowNet (a C# wrapper class for DS). I have a Windows Forms panel in my WPF window (via a WindowsFormsHost object) that I need the graph to render to. Here is the general flow of the app: The Start() method builds the graph and starts it; I pass the handle of my windows form panel and render to it using the IVideoWindow interface. Start() returns and the graph runs in the background. At some point, Stop() is called; this method stops the graph and destroys it.
Everything works fine as long as I call Start() and Stop() from the same thread. However, I will need to call them from different threads in my app. When this is the case, I get an exception in the part of code that destroys the graph (specifically, when I am attempting to enumerate the filters). I discovered that I need to use a Multithreaded Apartment when working with DirectShow. This is easy with a Windows Forms app; I just throw a [MTAThread] on my main method and everything works.
For my WPF app, this is apparently not an option. My workaround has been to launch new MTA threads when I need to call Start() and Stop(). This gets rid of the exception, but has introduced another problem. When the Start() method returns, the video disappears from the render panel. If I put a Sleep at the end of the Start() method, the video will be visible until the Sleep ends. In addition, I have verified that the graph continues to run after the video disappears. Does anyone have any advice as to how to proceed? Thanks.
Kevin
Which exception is thrown? I'm guessing something along the likes of: "The calling thread cannot access this object because a different thread owns it."
When this is the case, use a correct dispatcher to do your calls, as explained here.
FYI, Windows Forms doesn't support a MTAThread main thread. If it worked, then you just got lucky.
I believe you should be able to invoke DS objects from STA threads just fine - though I'm not that familiar with DS, it sounds like you're using windowless mode and it seems to me that it would work best with STA.
In that case, why not always call Start/Stop from your main thread? If another thread needs to tell the main thread to stop or start, then just have it queue a task to a TaskScheduler.FromCurrentSynchronizationContext to run it on the main thread.
Ok, so I've encountered a problem not too dissimilar before, but not with WPF, so take the following (very hacky) suggestion with a pinch of salt.
The following method basically creates an entirely separate application thread to run directshow commands in, but tells direct show to use the handle of the windows forms control hosted in your WPF application.
So, first we need a dummy WinForms form that we can use to invoke calls on, but that is never going to get rendered:
/// <summary>
/// Just a dummy invisible form.
/// </summary>
private class DummyForm : Form
{
protected override void SetVisibleCore(bool value)
{
//just override here, make sure that the form will never become visible
if (!IsHandleCreated)
{
CreateHandle();
}
value = false;
base.SetVisibleCore(value);
}
}
Next step is to create a thread that we can put a message loop on:
//this will need to be a class level variable, since all the directshow
//calls will get invoked on this form
DummyForm dumbForm;
Thread separateThread;
private void CreateDummyForm()
{
ManualResetEvent reset = new ManualResetEvent(false);
//create our thread
separateThread = new Thread((ThreadStart)
delegate
{
//we need a dummy form to invoke on
dumbForm = new DummyForm();
//signal the calling method that it can continue
reset.Set();
//now kick off the message loop
Application.Run(dumbForm);
});
//set the apartment state of this new thread to MTA
separateThread.SetApartmentState(ApartmentState.MTA);
separateThread.IsBackground = true;
separateThread.Start();
//we need to wait for the windowing thread to have initialised before we can
//say that initialisation is finished
reset.WaitOne();
//wait for the form handle to be created, since this won't happen until the form
//loads inside Application.Run
while (!dumbForm.IsHandleCreated)
{
Thread.Sleep(0);
}
}
So, once the dummy form (and its thread) have been created, you can invoke calls on the MTA
application thread like so:
/// <summary>
/// Blank delegate, used to represent any Invoke or BeginInvoke target method.
/// </summary>
public delegate void InvokeHandler();
//i'm assuming here that DSComponent is a class that all your directshow
//code is in, and externalControl is the WinForms control you have embedded in
//your application.
dumbForm.Invoke(new InvokeHandler(delegate
{
//maybe something like this?
DSComponent.Start(externalControl);
}));
//and to stop it...
dumbForm.Invoke(new InvokeHandler(delegate
{
DSComponent.Stop();
}));
Then, when you're all done with the Directshow stuff, shutdown your separate application thread like so:
//to end the separate thread and application loop,
//just close your invisible form
dumbForm.Close();
Advantage of this approach is that you neatly sandbox directshow into a separate thread. Disadvantage is the context switch of the Invoke calls, plus the overhead of having another application thread. You may have some fun shoehorning this into your current architecture, but it should help.
Let me know how you get on, I am intrigued as to how well this works.

C# Multi threading- Move objects between threads

i am working with a winforms control that is both a GUI element and also does some internal processing that has not been exposed to the developer. When this component is instantiated it may take between 5 and 15 seconds to become ready so what i want to do is put it on another thread and when its done bring it back to the gui thread and place it on my form. The problem is that this will (and has) cause a cross thread exception.
Normally when i work with worker threads its just with simple data objects i can push back when processing is complete and then use with controls already on the main thread but ive never needed to move an entire control in this fashion.
Does anyone know if this is possible and if so how? If not how does one deal with a problem like this where there is the potential to lock the main gui?
You don't need to lock the GUI, you just need to call invoke:
Controls in Windows Forms are bound to
a specific thread and are not thread
safe. Therefore, if you are calling a
control's method from a different
thread, you must use one of the
control's invoke methods to marshal
the call to the proper thread. This
property can be used to determine if
you must call an invoke method, which
can be useful if you do not know what
thread owns a control. ref
Here is how it looks in code:
public delegate void ComponentReadyDelegate(YourComponent component);
public void LoadComponent(YourComponent component)
{
if (this.InvokeRequired)
{
ComponentReadyDelegate e = new ComponentReadyDelegate(LoadComponent);
this.BeginInvoke(e, new object[]{component});
}
else
{
// The component is used by a UI control
component.DoSomething();
component.GetSomething();
}
}
// From the other thread just initialize the component
// and call the LoadComponent method on the GUI.
component.Initialize(); // 5-15 seconds
yourForm.LoadComponent(component);
Normally calling the LoadComponent from another thread will cause a cross-thread exception, but with the above implementation the method will be invoked on the GUI thread.
InvokeRequired tells you if:
the caller must call an invoke method
when making method calls to the
control because the caller is on a
different thread than the one the
control was created on.
ref
Update:
So if I understand you correctly the control object is created on a thread other than the GUI thread, therefore even if you were able to pass it to the GUI thread you still won't be able to use it without causing a cross-thread exception. The solution would be to create the object on the GUI thread, but initialize it on a separate thread:
public partial class MyForm : Form
{
public delegate void ComponentReadyDelegate(YourComponent component);
private YourComponent _component;
public MyForm()
{
InitializeComponent();
// The componet is created on the same thread as the GUI
_component = new YourComponent();
ThreadPool.QueueUserWorkItem(o =>
{
// The initialization takes 5-10 seconds
// so just initialize the component in separate thread
_component.Initialize();
LoadComponent(_component);
});
}
public void LoadComponent(YourComponent component)
{
if (this.InvokeRequired)
{
ComponentReadyDelegate e = new ComponentReadyDelegate(LoadComponent);
this.BeginInvoke(e, new object[]{component});
}
else
{
// The component is used by a UI control
component.DoSomething();
component.GetSomething();
}
}
}
Without knowing too much about the object. To avoid cross thread exceptions, you can make the initial thread invoke a call (Even if you are calling from a thread).
Copied and pasted from one of my own applications :
private delegate void UpdateStatusBoxDel(string status);
private void UpdateStatusBox(string status)
{
listBoxStats.Items.Add(status);
listBoxStats.SelectedIndex = listBoxStats.Items.Count - 1;
labelSuccessful.Text = SuccessfulSubmits.ToString();
labelFailed.Text = FailedSubmits.ToString();
}
private void UpdateStatusBoxAsync(string status)
{
if(!areWeStopping)
this.BeginInvoke(new UpdateStatusBoxDel(UpdateStatusBox), status);
}
So essentially the threaded task will call the "Async" method. Which will then tell the main form to begininvoke (Actually async itself).
I believe there is probably a shorter way to do all of this, without the need for creating delegates and two different methods. But this way is just ingrained into me. And it's what the Microsoft books teach to you do :p
The BackgroundWorker class is designed for exactly this situation. It will manage the thread for you, and let you start the thread, as well as cancel the thread. The thread can send events back to the GUI thread for status updates, or completion. The event handlers for these status and completion events are in the main GUI thread, and can update your WinForm controls. And the WinForm doesn't get locked. It's everything you need. (And works equally well in WPF and Silverlight, too.)
The control must be created and modified from the UI thread, there's no way around that.
In order to keep the UI responsive while doing long-running initialization, keep the process on a background thread and invoke any control access. The UI should remain responsive, but if it doesn't, you can add some wait time to the background thread. This is an example, using .Net 4 parallel tools: http://www.lovethedot.net/2009/01/parallel-programming-in-net-40-and_30.html
If interaction with the specific control being initialized can't be allowed until initialization finishes, then hide or disable it until complete.

C# Thread Pool Cross-Thread Communication

The Scenario
I have a windows forms application containing a MAINFORM with a listbox on it. The MAINFORM also has a THREAD POOL that creates new threads and fires them off to do lots of different bits of processing. Whilst each of these different worker threads is doing its job, I want to report this progress back to my MAINFORM, however, I can't because it requires Cross-Thread communication.
Progress
So far all of the tutorials etc. that I have seen relating to this topic involve custom(ish) threading implementations, whereas I literally have a fairly basic(ish) standard THREAD POOL implementation. Since I don't want to really modify any of my code (since the application runs like a beast with no quarms) - I'm after some advice as to how I can go about doing this cross-thread communication. ALTERNATIVELY - How to implement a different "LOGTOSCREEN" method altogether (obviously still bearing in mind the cross-thread communication thing).
WARNING:
I use this website at work, where we are locked down to IE6 only, and the javascript thus fails, meaning I cannot click accept on any answers during work, and thus my acceptance rate is low. I can't do anything about it I'm afraid, sorry.
EDIT:
I DO NOT HAVE INSTALL RIGHTS ON MY COMPUTER AT WORK.
I do have firefox but the proxy at work fails when using this site on firefox.
And no, funnily enough, I don't have the internet at home, I literally just moved to this city and the flat is a new build, so the address hasn't been registered with the post office, and thus the phone company cannot find the address on their system till they send a surveyor out, smarty pants.
FURTHER EDIT:
I DO NOT WANT TO CHANGE MY THREADING IMPLEMENTATION. AT ALL! - Accept to enable cross-thread communication....why would a backgroundworker help here!?
CODE RELATED EDIT:
Does it make a difference that when my THREAD POOL executes the new threads, it creates a new instance of a class and calls the entire thing on that new thread........i.e. your code example doesn't quite fit....i think?
Use the BackgroundWorker class in .NET and use the ProgressChanged and RunWorkerCompleted events to communicate back to your UI thread
Edit:
Sounds like you don't like BackgroundWorker, or just don't want to refactor. In that case, you have to check the InvokeRequired property on your form or one of your controls and if it is true, then you have to call Control.Invoke to force your UI update logic to occur on your main thread.
here is an example:
private void MyThreadFunction()
{
if (!InvokeRequired)
{
myLabel.Text = "You pushed the button!";
}
else
{
Invoke(new ThreadStart(MyThreadFunction));
}
}
You can use any delegate type to pass to Invoke, because it takes optional parameters that can be passed to your delegate when it is invoked on the main thread.
You could do something like this:
class MyForm : Form
{
private Label label = new Label();
private void DoWork()
{
// Do work ... Not in UI thread
// Update label... In UI thread
this.Invoke(new MethodInvoker(() => label.Text = "New Text!"));
}
}
The DoWork method it's the one running in your worker threads. You could check if an invoke is required using InvokeRequired property, but the assumption is that your code is running on worker threads so the invoke will always be required.
You can do this using delegate object.
So you would create a callback method in your MAIN form and let your CHILD forms call this method using delegates when they are done processing.
Try using Control.BeginInvoke to queue your update to the UI on the UI thread.

Multithreaded message pumping without second form

I have a C# application which uses a COM component. This COM component require a message pump (Application.Run()) to do its processing. This means it's been stuck on the main thread. But I recently discovered that it's possible to start another Application.Run on another thread which gets its own ApplicationContext.
So I want to host the COM component on its own thread inside it's own Application.Run(), but I can't figure out how to get things started on the new thread without creating a UI form.
The WindowsFormsSynchronizationContext I need to communicate with the thread doesn't get created until Application.Run(). But once Application.Run() is called, I can't figure out how to get at the SynchronizationContext. If I could just raise a single event on that thread, I could use that to bootstrap the whole thing (create the COM object, etc.), but there doesn't seem to be anywhere to hook into the new event loop without a form.
I've tried all kinds of convoluted things, like installing a message filter (no messages get raised on the new thread), copying the execution context into another thread and trying to retrieve the SynchronizationContext from there (it refuses to copy the ExecutionContext of an already running thread), retrieving Thread.CurrentContext before starting Application.Run() and then calling DoCallbBack() (the DoCallback ends up on the original thread), etc. Nothing I've tried works.
Bryce,
You might be able to adapt this snippet from Anders Hejlsberg's talk about "The Future of C#". It's a little class that adds a message pump to a thread so that he can open windows using a REPL loop, and they will have a message pump attached to them.
The code looks like this:
using System.Windows.Forms;
using System.Threading;
class UserInterfaceThread()
{
static Form window;
public UserInterfaceThread()
{
var thread = new Thread(() => {
window = new Form();
var handle = window.Handle;
Application.Run();
});
thread.Start();
}
public void Run(Action action)
{
window.Invoke(action);
}
}
The discussion relating to this code occurs at 1 hour 5 minutes into Anders' talk, if you want to review it.

Categories

Resources