Set ApartmentState on a Task

38,141

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

Share:
38,141

Related videos on Youtube

Luke101
Author by

Luke101

Updated on June 09, 2020

Comments

  • Luke101
    Luke101 almost 4 years

    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++)
    {
         Task t = Task.Factory.StartNew(zom[i].Process);
         t.Wait();
    }
    
    • Mike Perrenoud
      Mike Perrenoud almost 11 years
    • Luke101
      Luke101 almost 11 years
      Yes, I have looked at the code in your link already but still cannot get my code above to work using the continuwwith.
    • Mike Perrenoud
      Mike Perrenoud almost 11 years
      So what happens if you implement it like it was in the answer provided at that link?
    • user1703401
      user1703401 almost 11 years
      You would only be interested in that if you wanted an STA. That's the antipode of a task, an STA thread can't be a threadpool thread and must pump a message loop. You'll need a regular Thread, call its SetApartmentState() method and Application.Run().
  • Alexander Pacha
    Alexander Pacha about 9 years
    Is this really setting the Apartment state!?
  • John
    John about 9 years
    Shouldn't you add a System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke‌​Shutdown() after the try-catch to clean up the dispatcher resources?
  • Servy
    Servy about 9 years
    @John There no dispatcher to shut down. None was ever created. This method isn't even necessarily used in a WPF context.
  • John
    John about 9 years
    I see. I personally need it for WPF - so in my case I should insert the InvokeShutdown, right?
  • Servy
    Servy about 9 years
    @John Why would you? You have no dispatcher to shut down, as none is created here.
  • John
    John about 9 years
    I was under the impression that using WPF controls requires and creates a dispatcher (for doing out-of-line layouting, for example). I might be wrong though.
  • Servy
    Servy about 9 years
    @John Creating a dispatcher creates a dispatcher. None of the code in this answer creates a dispatcher. If the delegate you pass to this creates one, it'll need to clean it up. Or if you edit the code to create a dispatcher, then you'll need to clean it up.
  • John
    John about 9 years
    "If you attempt to get the CurrentDispatcher for the current thread and a Dispatcher is not associated with the thread, a Dispatcher will be created." - from msdn. That means it's very likely you're going to have one if you do WPF-related stuff, doesn't it?
  • Servy
    Servy about 9 years
    Then again, I stand by my statement. If you're executing code that's creating a dispatcher, then you'll need to shut it down. This code is not creating a dispatcher, and as such, isn't going to try to clean up a resource that is entirely outside of its scope. If you're using this in conjunction with a WPF and creating a dispatcher that requires cleaning up, then go clean it up. It has nothing to do with this method, rather it is a requirement for your particular delegate that you pass to it.
  • John
    John about 9 years
    Hence my question having been "I personally need it for WPF - so in my case I should insert the InvokeShutdown, right?". But thanks for nothing.
  • Servy
    Servy about 9 years
    @John It doesn't belong in this method, no. If your delegate is creating a resource that it needs to clean up, it should clean it up. It has nothing whatsoever to do with how you create a task that runs a delegate on an STA thread. If you want to create a separate method that is specifically designed for your specific circumstances, and in those circumstances you have some unique business requirements you can do whatever you want in such a method.
  • grapkulec
    grapkulec about 9 years
    oh my frakking god! thank you for this snippet!!! I spent last 2 hours looking for this because even though I implemented MessageFilter according to msdn I was still getting that frakking 'busy' exception and filter methods weren't called at all. all was ok until I was running this in plain command line but then I wanted little UI to provide parameters for solution generation in more convenient way and have progress updates on UI from task executing DTE stuff. and hell broke loose... THANK YOU!
  • Ekevoo
    Ekevoo over 8 years
    This will only throw the first exception from ex.InnerExceptions.
  • Ekevoo
    Ekevoo over 8 years
    @John You should clean up resources in the same scope you created them. If you create a dispatcher elsewhere, clean it up there.
  • Jürgen Steinblock
    Jürgen Steinblock over 7 years
    @Servy I think it would be a good idea to set thread.IsBackground = true; as Task's use background threads, too.
  • Servy
    Servy over 7 years
    @JürgenSteinblock Clearly the purpose of something like this is not designed to be used by a background thread. It would very likely be wrong for just about any usage in this context. While it is true that an operation like Task.Run is specifically designed to represent asynchronous worker processes performing work in another thread, that doesn't mean tasks necessarily represent the use of background threads. They do no. They represent asynchronous operations.
  • Jürgen Steinblock
    Jürgen Steinblock over 7 years
    @Servy Well Background threads are identical to foreground threads, except that background threads do not prevent a process from terminating. (from MSDN). If the func will hang and if you close your main form (for a winforms app) the process will still be alive. TPL Tasks internally uses background threads, so it would be a good idea to set the IsBackground property to be compatible (I would expect a Task to shutdown if I unload an AppDomain.
  • Servy
    Servy over 7 years
    @JürgenSteinblock I'm well aware what a background task means. If the operation being performed here requires an STA thread, odds a very high it's not a background operation, but a foreground operation, and as such, the application should not shut down if it is the only non-background thread. Again, that other task returning operations do logically represent background work doesn't mean all task returning operations represent background work. If you unload the app domain then this operation would stop. Rather, the app domain won't shut itself down while this operation is running.
  • JobaDiniz
    JobaDiniz almost 6 years
    what about a overload with CancellationToken?
  • Servy
    Servy almost 6 years
    @JobaDiniz Just have the function close over the cancellation token if you need one. There's no reason for this function to be aware of the cancellation token that it might be using.
  • JobaDiniz
    JobaDiniz almost 6 years
    how come? I need to abort the thread thread.Abort when cancellation is requested... I changed the code with cancellation.Register(thread.Abort) and cancellation.Register(tsc.SetCancelled)
  • Servy
    Servy almost 6 years
    @JobaDiniz You most certainly should not be aborting threads. It's an extremely dangerous thing to be doing. Have whatever work your doing actually respond to the cancellation token if you want it to be able to stop its work when the cancellation token indicates as much.
  • JobaDiniz
    JobaDiniz almost 6 years
    You're right... In the Task.Run(action, cancellationToken) the token is only used to verify whether the thread should be started, because if the token is already cancelled, the thread shouldn't start. So that should be also the behavior here.
  • Servy
    Servy almost 6 years
    @JobaDiniz I've never found that overload to be useful (it almost exclusively just confuses people into thinking that it's a non-cooperative cancellation, which it isn't), but if you feel that it's useful in your situation to have a cancellation token that you use to determine if you should start the thread then sure, you could add that to your solution.
  • Fred
    Fred over 5 years
    Could you provide an example of how to use this with a function that has several parameters? My code right now looks like this: var result = await Task.Run(() => MyMethod(a, b, c)); But I'd like to run this task using STA.
  • Muhannad
    Muhannad over 5 years
    I used this one and it worked for me I was able to create (in the Action) a new WPF UserControl that has processing on its xaml.ca. Then I print it using PrintVisual without having the nasty exception of STAThread. Thank you
  • Stephen Cleary
    Stephen Cleary almost 3 years
    This code sets the apartment state to STA but does not do pumping, which is a requirement of STA threads. So there's an implicit assumption that func will do pumping. Use with care.