DirectShow/WPF Threading issue - c#

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.

Related

C# Getting to original thread to set textbox values

I've got my main form Form1 running the main bulk of my program.
I have a separate thread started to perform an algorithm.
When I run the method from the new thread, method MyAlgorithm() I get the error
InvalidOperationException with the message, "Control control name accessed from a thread other than the thread it was created on."
How do I get back to the original thread so that I can run the method to update my text boxes with the latest values?
This is the method that I want to run contained in Form1, the main class in my application.
// Reset the results values
public void ShowResults()
{
while (true)
{
loopsNum.Text = Convert.ToString(resultLoopsNum);
nodesVisitedNum.Text = Convert.ToString(resultNodesVisitedNum);
nodesResolvedNum.Text = Convert.ToString(resultNodesResolvedNum);
cpuLoopsNum.Text = Convert.ToString(resultCpuLoopsNum);
shortestPathCostNum.Text = Convert.ToString(resultShortestPathCost);
}
}
I've looked at the Invoke() methods, but I don't know how to get the original instance of my Form1 from the threaded method.
I'm invoking my thread like this...
// Set the algorithm method up in it's own thread
Thread thread = new Thread(new ThreadStart(MyAlgorithm));
// Run the algorithm
thread.Start();
How do I get back to the original thread so that I can run the method to update my text boxes with the latest values?
In Windows Forms, you'd either use Control.Invoke/BeginInvoke or use a BackgroundWorker and perform the update in the progress event handler.
In WPF you'd use Dispatcher.Invoke/BeginInvoke.
In C# 5 and .NET 4.5 you'll be able to use async methods which should make a lot of this much simpler...
I've looked at the Invoke() methods, but I don't know how to get the original instance of my Form1 from the threaded method.
If the "threaded method" is just an instance method of the Form, then you've already got the this reference. If it's not, you'll need to provide that information - ideally as an ISynchronizeInvoke to avoid a direct dependency on Windows Forms if you can express the "update" part separately. (That interface is somewhat deprecated these days, in favour of synchronization contexts, but it still works perfectly well.)
Have a look at Control.Invoke():
public void ShowResults()
{
while (true)
{
Thread.Sleep(1000); // don't spam the UI thread
if (this.InvokeRequired)
{
this.Invoke((Action)UpdateGui);
}
else
{
UpdateGui();
}
}
}
private void UpdateGui()
{
loopsNum.Text = Convert.ToString(resultLoopsNum);
nodesVisitedNum.Text = Convert.ToString(resultNodesVisitedNum);
nodesResolvedNum.Text = Convert.ToString(resultNodesResolvedNum);
cpuLoopsNum.Text = Convert.ToString(resultCpuLoopsNum);
shortestPathCostNum.Text = Convert.ToString(resultShortestPathCost);
}
You can use:
myform.Invoke(ShowResults);
There's other options here too:
Alternately use a System.Forms.Timer to call ShowResults periodically. Or another option would be not to use another thread to do the operation; do it in the GUI thread and call Application.DoEvents() from within the operation when you want to let the GUI update.
The first option is nice because it keeps you from accidentally flooding the GUI with Invoke requests, and the second option is nice because it's all on the GUI thread and allows you to have fine-grain control over when things get displayed on the GUI.

Having trouble creating a form on a second thread

I'm writing a plug-in for another application through C#.NET. Some of the processes my plug-in must perform are rather time consuming so I want to take advantage of multiple threads so I can show the user a progress bar of how the current task if progressing rather then the whole thing just hanging.
Typically the UI for something like this would be created in the main thread, and a secondary thread would be created to do the work, such as through the BackGroundWorker class. However, in my case the work must be done in the main thread because the application I'm writing the plug-in for isn't to happy with threads other then the thread it created for the plug-in accessing it.
So instead I'm creating a second thread to create my UI in (a WinForms Form), which then communicates back to the main thread to do any real work.
I'm able to create my Form in the main thread just fine, yet when I try to instantiate my form in the second thread I get an InvalidOperationException. This occurs in the designer file for the form where the name property of a column in a list view is being set.
Here are the details of the exception.
System.InvalidOperationException was caught
Message=ColumnInfo cannot be set.
Source=System.Windows.Forms
StackTrace:
at System.Windows.Forms.ListView.SetColumnInfo(Int32 mask, ColumnHeader ch)
at System.Windows.Forms.ColumnHeader.set_Text(String value)
at QA.Revit.RevitQAForm.InitializeComponent() in C:\Documents and Settings\eric.anastas\My Documents\_SVN WC\QA Tool\RevitModelCheckerPlugIn\RevitQAForm.Designer.cs:line 758
at QA.Revit.RevitQAForm..ctor() in C:\Documents and Settings\eric.anastas\My Documents\_SVN WC\QA Tool\RevitModelCheckerPlugIn\RevitQAForm.cs:line 34
at QA.Revit.RevitQAToolApp.FormMethod() in C:\Documents and Settings\eric.anastas\My Documents\_SVN WC\QA Tool\RevitModelCheckerPlugIn\RevitModelCheckerCmd.cs:line 99
InnerException:
Update
I seemed to have gotten this working now by changing the ApartmentState of the secondary UI thread to STA. Although I'm totaly new to this multithreading stuff and have no idea what ApartmentState or STA means.
Here's my code.
//property used to store a reference to the form
internal RevitQAForm RevitQAForm { get; set; }
//monitor object that when pulsed shows the form
public static readonly Object showFormLock = new object();
//this method is called by the parent app when it starts
public Autodesk.Revit.UI.Result OnStartup(Autodesk.Revit.UI.UIControlledApplication application)
{
//this creates the form UI Thread
_formThread = new System.Threading.Thread(new System.Threading.ThreadStart(FormMethod));
_formThread.Name = "Form Thread";
_formThread.SetApartmentState(System.Threading.ApartmentState.STA);
_formThread.Start();
//returns that the plug-in startup succeeded
return Autodesk.Revit.UI.Result.Succeeded;
}
//the method is started on the second thread
private void FormMethod()
{
try
{
//creates the form
RevitQAForm = new RevitQAForm();
lock (showFormLock)
{
while (true)
{
//waits for a pulse
System.Threading.Monitor.Wait(showFormLock);
RevitQAForm.ShowDialog();
}
}
}
catch (System.Threading.ThreadAbortException)
{
//disposes the form if the thread is aborted
RevitQAForm.Dispose();
}
}
//this is called when the user request the form be shown
public void ShowForm()
{
lock (showFormLock)
{
System.Threading.Monitor.Pulse(showFormLock);
}
}
//this is called when the program closes
public Autodesk.Revit.UI.Result OnShutdown(Autodesk.Revit.UI.UIControlledApplication application)
{
//aborts the form thread
formThread.Abort();
return Autodesk.Revit.UI.Result.Succeeded;
}
Like I said this seems to work now. I'm able to start the app with my plug-in and show the form repeatedly. The form is also disposed when I close the program.
Yet now I'm trying to figure out how this form can communicate back to the main thread. The form will need to be able to trigger the main thread to start processing, the main thread will then need to be able to periodically report it's progress back to the form thread. At any point the form thread should be able to tell the main thread to cancel processing. Finally the main thread will need to notify the form when the processing is complete.
Any one have any tips on how I could do this?
This won't work. All forms need to use the underlying message pump in Windows, and to do that they need to be on the original thread.
To trigger the processing in main thread, you can use any WaitHandle derived class such as say ManualResetEvent/AutoResetEvent - essentially, Main thread will wait on to the wait handle and form thread can signal the event to start processing.
For communicating progress back from main thread to your UI/Form thread, you can use events or delegates. The simplest way would be to declare the process update delegate, instantiate it with some form's method. Then main thread can invoke it - which will essentially run the method within your form class (on main thread). Within this method, you must need to marshall call to your form's thread using Invoke method the form.
Try to call method, which uses
System.Windows.Forms.ListView.SetColumnInfo(Int32 mask, ColumnHeader ch)
by using method Invoke.

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.

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.

How can a background thread hang the UI thread?

I am using a background thread to initialize an instrument over USB. The UI hangs when I try to open the device. I would expect the background thread to pause when calling Open on the device, but not the UI thread. I am testing this with no UI interaction from the background thread. I don't know how to debug the problem, and it's too broad a question, but perhaps someone has seen something like this before. There is nothing wrong with the ActiveX interop as far as I know, the device works correctly. This is the general approach:
using System;
using FancyVoltmeterLibrary;
namespace SOQuestion
{
public class MeterClass
{
private FancyVoltmeter meter;
private Thread meterThread;
public MeterClass()
{
// Create instance of ActiveX/COM object.
meter = new FancyVoltmeter();
meterThread = new Thread(UpdateMeter);
meterThread.Name = "Meter Thread";
meterThread.Priority = ThreadPriority.Normal;
meterThread.IsBackground = true;
meterThread.Start();
}
private void UpdateMeter()
{
while(true)
{
Thread.Sleep(1000);
if(!meter.IsOpen())
{
// Meter may be powered off here.
// The call to Open takes about 1 second.
// UI hangs during the call???
meter.Open();
}
// code to read meter goes here.
}
}
}
}
Edit: Perhaps unclear what I meant. By 'hang' I should say 'freezes momentarily'.
Does meter require running in an STA? Is the call to Open() actually being marshalled back to the UI thread for this reason?
You can verify this is true by looking at the callstack of the hung UI thread in the debugger.
How long time does the instantiation of the FancyVoltmeter take? Could it be that it is not the Open method that causes the UI freeze, but creating the COM object (which is done on the UI thread)?
If that turns out to be the case, moving the creation of this object to happen on the new, separate worker thread should take care of the problem.
Edit: I saw now that you already found this out in your comment to Michael...
I would suggest you wrap the call to meter.open() in a separate method, and call that method from within the updateMeter() method using Invoke() or BeginInvoke() construct on the form or parent control. Doing this will marshal the action back on to the UI thread and should execute gracefully. I hope this helps.
Consider using a BackgroundWorker for this task.

Categories

Resources