I am trying to set the apartment state on a task but see no option in doing this. Is there a way to do this using a Task?
for (int i = 0; i < zom.Count; i
You could for example make a new task as follows:
try
{
Task reportTask = Task.Factory.StartNew(
() =>
{
Report report = new Report(this._manager);
report.ExporterPDF();
}
, CancellationToken.None
, TaskCreationOptions.None
, TaskScheduler.FromCurrentSynchronizationContext()
);
reportTask.Wait();
}
catch (AggregateException ex)
{
foreach(var exception in ex.InnerExceptions)
{
throw ex.InnerException;
}
}
This is what I'm using with Action since I don't need to return anything:
public static class TaskUtil
{
public static Task StartSTATask(Action action)
{
var tcs = new TaskCompletionSource<object>();
var thread = new Thread(() =>
{
try
{
action();
tcs.SetResult(new object());
}
catch (Exception e)
{
tcs.SetException(e);
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return tcs.Task;
}
}
Where I call it like this:
TaskUtil.StartSTATask(async () => await RefreshRecords());
For details, please see https://github.com/xunit/xunit/issues/103 and Func vs. Action vs. Predicate
FYI, this is the exception I was getting where I needed to set the apartment state:
System.InvalidOperationException occurred HResult=-2146233079
Message=The calling thread must be STA, because many UI components require this. Source=PresentationCore StackTrace: at System.Windows.Input.InputManager..ctor() at System.Windows.Input.InputManager.GetCurrentInputManagerImpl() at System.Windows.Input.Keyboard.ClearFocus()
If Task needs to be provided with a CancellationToken, STA thread can be started within a task action and joined as shown below:
var task = Task.Factory.StartNew(() =>
{
var tcs = new TaskCompletionSource<object>();
var thread = new Thread(() =>
{
try
{
action();
tcs.SetResult(new object());
}
catch (Exception e)
{
tcs.SetException(e);
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
}, cancellationToken);
When StartNew
fails you just do it yourself:
public static Task<T> StartSTATask<T>(Func<T> func)
{
var tcs = new TaskCompletionSource<T>();
Thread thread = new Thread(() =>
{
try
{
tcs.SetResult(func());
}
catch (Exception e)
{
tcs.SetException(e);
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return tcs.Task;
}
(You can create one for Task
that will look almost identical, or add overloads for some of the various options that StartNew
has.)
This is a good use case for the Task
constructor, and the RunSynchronously method.
public static Task<T> RunSTATask<T>(Func<T> function)
{
var task = new Task<T>(function, TaskCreationOptions.DenyChildAttach);
var thread = new Thread(task.RunSynchronously);
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return task;
}
The purpose of TaskCreationOptions.DenyChildAttach
is to make the resulting task behave identically to the Servy's solution (attaching a child task to a parent TaskCompletionSource.Task is not possible). Denying children to be attached is also the behavior of the Task.Run method.
An overload of Servy's answer to start a void Task
public static Task StartSTATask(Action func)
{
var tcs = new TaskCompletionSource<object>();
var thread = new Thread(() =>
{
try
{
func();
tcs.SetResult(null);
}
catch (Exception e)
{
tcs.SetException(e);
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return tcs.Task;
}