Reputation: 11498
I'm working with ExcelDna and async functions. If there's an exception in the async:d code I want to show a fancy WPF error window. My problem is that I'm getting the error "The calling thread must be STA, because many UI components require this." How can I solve this?
[ExcelFunction(Description = "", Category = "")]
public static async Task<object> /*string*/ Foo(CancellationToken ct)
{
try
{
return await Task.Run(async () =>
{
await Task.Delay(1000, ct);
throw new Exception("BOO");
return "HelloWorld";
}, ct2.Token);
}
catch (Exception e)
{
return ShowWpfErrorWindowThatRequiresSTA(e);
}
}
Upvotes: 1
Views: 2658
Reputation: 16907
When your Excel function runs there is no SynchronizationContext.Current
installed, so the async/await mechanism will runs the code after await
(including your catch handler) on a ThreadPool thread. That's not a context where you can directly show your WPF form.
Installing a DispatcherSynchronizationContext corresponding to a Dispatcher running on the main thread (or another thread) would work, but you have to do that for every UDF call. Somehow the native code path through Excel loses the .NET call context on the main thread, so the SynchronizationContext gets lost.
Better is probably to assume that the catch handler is running on a ThreadPool thread, and make a SynchronizationContext.Post
call from the catch handler to take you back to the main thread running your Dispatcher and WPF form.
You can look at how Excel-DNA implements the (WinForms) LogDisplay window. (https://github.com/Excel-DNA/ExcelDna/blob/master/Source/ExcelDna.Integration/LogDisplay.cs). You can call LogDisplay.WriteLine(...)
from any thread, and it will do a _syncContext.Post
to run the 'Show' on the main thread.
The C# async/await mechanism works less well with Excel since the native/managed transitions, and whatever Excel does internally, messes up the thread context that needs to flow between continuations. Even on the .NET side, it's not clear how thread context is managed between AppDomains (different Excel add-ins). So it's best not to rely on the .NET runtime being able to thread any kind of context through the managed/native transitions.
Upvotes: 3
Reputation: 456537
Many Office plugins have a problem where SynchronizationContext.Current
is null
, and asynchronous continuations execute on the thread pool. I'd check the value of SynchronizationContext.Current
before the first await
.
I have had some success in creating a WinFormsSynchronizationContext
and installing that on the thread before the first await
. However, installing a WPF context would be more complex.
Upvotes: 2