简体   繁体   English

ExcelDna:异步:调用线程必须是STA

[英]ExcelDna: Async: The calling thread must be STA

I'm working with ExcelDna and async functions. 我正在使用ExcelDna和异步功能。 If there's an exception in the async:d code I want to show a fancy WPF error window. 如果async:d代码中有异常,我想显示一个奇特的WPF错误窗口。 My problem is that I'm getting the error "The calling thread must be STA, because many UI components require this." 我的问题是,我收到错误消息“调用线程必须是STA,因为许多UI组件都需要STA。” 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);
        }
    }

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. 当您的Excel函数运行时,没有安装SynchronizationContext.Current ,因此async / await机制将在ThreadPool线程上的await (包括您的catch处理程序)之后运行代码。 That's not a context where you can directly show your WPF form. 那不是您可以直接显示WPF表单的上下文。

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. 安装与在主线程(或另一个线程)上运行的Dispatcher相对应的DispatcherSynchronizationContext是可行的,但是您必须为每个UDF调用执行此操作。 Somehow the native code path through Excel loses the .NET call context on the main thread, so the SynchronizationContext gets lost. 通过Excel的本机代码路径以某种方式丢失了主线程上的.NET调用上下文,因此SynchronizationContext丢失了。

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. 最好假设捕获处理程序正在ThreadPool线程上运行,并从捕获处理程序进行SynchronizationContext.Post调用,以使您回到运行Dispatcher和WPF表单的主线程。

You can look at how Excel-DNA implements the (WinForms) LogDisplay window. 您可以查看Excel-DNA如何实现(WinForms)LogDisplay窗口。 ( https://github.com/Excel-DNA/ExcelDna/blob/master/Source/ExcelDna.Integration/LogDisplay.cs ). 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. 您可以从任何线程调用LogDisplay.WriteLine(...) ,它将执行_syncContext.Post以在主线程上运行“ Show”。

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. C#异步/等待机制在Excel中的效果较差,因为本机/托管的转换以及Excel在内部所做的任何事情都弄乱了需要在延续之间流动的线程上下文。 Even on the .NET side, it's not clear how thread context is managed between AppDomains (different Excel add-ins). 即使在.NET方面,也不清楚如何在AppDomain(不同的Excel加载项)之间管理线程上下文。 So it's best not to rely on the .NET runtime being able to thread any kind of context through the managed/native transitions. 因此,最好不要依赖.NET运行时能够通过托管/本机转换线程化任何类型的上下文。

Many Office plugins have a problem where SynchronizationContext.Current is null , and asynchronous continuations execute on the thread pool. 许多Office插件都有一个问题,其中SynchronizationContext.Currentnull ,并且异步继续在线程池上执行。 I'd check the value of SynchronizationContext.Current before the first await . 我将在第一次await之前检查SynchronizationContext.Current的值。

I have had some success in creating a WinFormsSynchronizationContext and installing that on the thread before the first await . 我曾在创造一些成功WinFormsSynchronizationContext和第一,然后再安装该线程上await However, installing a WPF context would be more complex. 但是,安装WPF上下文会更加复杂。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM