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

ExcelDna: Async: The calling thread must be STA

我正在使用 ExcelDna 和异步函数。如果 async:d 代码中存在异常,我想显示一个奇特的 WPF 错误 window。我的问题是出现错误 "The calling thread must be STA, because many UI components require 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);
        }
    }

许多 Office 插件有一个问题,其中 SynchronizationContext.Currentnull,异步延续在线程池上执行。我会在第一个 await.

之前检查 SynchronizationContext.Current 的值

我在创建 WinFormsSynchronizationContext 并将其安装在第一个 await 之前的线程上取得了一些成功。但是,安装 WPF 上下文会更复杂。

当您的 Excel 函数 运行s 没有安装 SynchronizationContext.Current 时,因此 async/await 机制将 运行s [=11 之后的代码=](包括您的捕获处理程序)在 ThreadPool 线程上。这不是您可以直接显示 WPF 表单的上下文。

在主线程(或其他线程)上安装与 Dispatcher 运行ning 相对应的 DispatcherSynchronizationContext 是可行的,但您必须为每个 UDF 调用执行此操作。通过 Excel 的本机代码路径以某种方式丢失了主线程上的 .NET 调用上下文,因此 SynchronizationContext 丢失了。

更好的做法可能是假设 catch 处理程序 运行 在线程池线程上运行,然后从 catch 处理程序进行 SynchronizationContext.Post 调用以将您带回主线程 运行设置您的 Dispatcher 和 WPF 表单。

您可以查看 Excel-DNA 如何实现 (WinForms) LogDisplay window。 (https://github.com/Excel-DNA/ExcelDna/blob/master/Source/ExcelDna.Integration/LogDisplay.cs)。您可以从任何线程调用 LogDisplay.WriteLine(...),它会在主线程上执行 _syncContext.Post 到 运行 和 'Show'。

C# async/await 机制与 Excel 的工作效果较差,因为 native/managed 转换,无论 Excel 在内部做什么,都会弄乱需要流动的线程上下文连续之间。即使在 .NET 方面,也不清楚如何在 AppDomain(不同的 Excel 加载项)之间管理线程上下文。因此,最好不要依赖 .NET 运行时间能够通过 managed/native 转换对任何类型的上下文进行线程处理。