I want to execute some long lasting tasks in a C# gui application (Windows Forms based).
I first tried to execute these in parallel with Task.Factory.StartNew()
.
The tasks involve COM interaction. When I execute the task (which should have been executed in parallel) the main/gui thread shows the busy cursor and does not respond.
The object through which the COM calls are made was created in the main thread which uses the single threaded apartment. In these posts:
I read that this might be the reason.
So my question is: If this is the reason what would be a remedy?
Here is what the code looks like (simplified):
// the Main() method is a STA which could be important?
[STAThread]
static void Main()
{
//...
}
// inside the MainForm class
private AComWrapperCreatedByCSharp comWrapper;
private void button1Click(object sender, EventArgs e)
{
var myCancellationSource = new CancellationTokenSource();
if (someCondition)
{
// both alternatives did not work
//Task.Factory.StartNew(
ThreadPool.QueueUserWorkItem(
_ =>
{
try
{
CancellationToken cancellationToken = myCancellationSource.Token;
// the main thread will respond while this tasks sleeps!
Thread.Sleep(5000);
cancellationToken.ThrowIfCancellationRequested();
// that's where the task's thread is when I pause execution
// while the gui is not responding
var result = comWrapper.CallSeveralLongerLastingCOMOperations();
cancellationToken.ThrowIfCancellationRequested();
this.BeginInvoke(new UpdateDelegate(() =>
{
handle(result);
}));
}
catch (OperationCanceledException)
{
return;
}
catch (Exception ex)
{
threadSafeLogging(string.Format(@"An exception occurred: ""{0}""", ex));
return;
}
}
// I tried adding these lines when starting the Task.Factory.... solution
//,"factory started thread",_updateProjectsDevicesAndLibrariesTaskCancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default
);
}
}
Thanks
This is the solution that @Hans Passant mentioned:
// inside the MainForm class
// the COM access object is no longer created in the main thread
private AComWrapperCreatedByCSharp comWrapper; // = new AComWrapperCreatedByCSharp;
private void someInitializationMethodInTheMainThread()
{
var th = new Thread(() =>
{
comWrapper = CreateComWrapper();
});
th.SetApartmentState(ApartmentState.MTA);
th.Start();
th.Join();
}
If I got it right, the only purpose of this thread is to create an object in the not STA of the main thread!