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()
Related videos on Youtube
Luke101
Updated on June 09, 2020Comments
-
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 almost 11 years
-
Luke101 almost 11 yearsYes, I have looked at the code in your link already but still cannot get my code above to work using the continuwwith.
-
Mike Perrenoud almost 11 yearsSo what happens if you implement it like it was in the answer provided at that link?
-
user1703401 almost 11 yearsYou 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 about 9 yearsIs this really setting the Apartment state!?
-
John about 9 yearsShouldn't you add a
System.Windows.Threading.Dispatcher.CurrentDispatcher.InvokeShutdown()
after the try-catch to clean up the dispatcher resources? -
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 about 9 yearsI see. I personally need it for WPF - so in my case I should insert the
InvokeShutdown
, right? -
Servy about 9 years@John Why would you? You have no dispatcher to shut down, as none is created here.
-
John about 9 yearsI 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 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 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 about 9 yearsThen 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 about 9 yearsHence 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 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 about 9 yearsoh 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 over 8 yearsThis will only throw the first exception from ex.InnerExceptions.
-
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 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 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 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 thefunc
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 theIsBackground
property to be compatible (I would expect a Task to shutdown if I unload an AppDomain. -
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 almost 6 yearswhat about a overload with
CancellationToken
? -
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 almost 6 yearshow come? I need to abort the thread
thread.Abort
when cancellation is requested... I changed the code withcancellation.Register(thread.Abort)
andcancellation.Register(tsc.SetCancelled)
-
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 almost 6 yearsYou'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 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 over 5 yearsCould 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 over 5 yearsI 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 almost 3 yearsThis 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.