Set ApartmentState on a Task

Solution 1:

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.)

Solution 2:

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;
}

Solution 3:

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;
            }
        }

Solution 4:

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.

Solution 5:

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()