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

时间:2016-04-07 19:38:51

标签: c# excel-dna

我正在使用ExcelDna和异步功能。如果async:d代码中有异常,我想显示一个花哨的WPF错误窗口。我的问题是我得到错误"调用线程必须是STA,因为许多UI组件都需要这个。"我怎么解决这个问题?

    [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);
        }
    }

2 个答案:

答案 0 :(得分:3)

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

安装与主线程(或另一个线程)上运行的Dispatcher相对应的DispatcherSynchronizationContext会起作用,但是您必须为每个UDF调用执行此操作。不知何故,通过Excel的本机代码路径丢失了主线程上的.NET调用上下文,因此SynchronizationContext丢失了。

最好可能假设catch处理程序在ThreadPool线程上运行,并从catch处理程序进行SynchronizationContext.Post调用,将您带回运行Dispatcher和WPF表单的主线程。

您可以查看Excel-DNA如何实现(WinForms)LogDisplay窗口。 (https://github.com/Excel-DNA/ExcelDna/blob/master/Source/ExcelDna.Integration/LogDisplay.cs)。您可以从任何主题调用LogDisplay.WriteLine(...),它会_syncContext.Post运行&#39;显示&#39}。在主线上。

C#async / await机制对Excel的效果不太好,因为本机/托管转换以及Excel内部的任何操作都会破坏需要在continuation之间流动的线程上下文。即使在.NET方面,也不清楚如何在AppDomains(不同的Excel加载项)之间管理线程上下文。因此,最好不要依赖.NET运行时能够通过托管/本机转换来处理任何类型的上下文。

答案 1 :(得分:2)

许多Office插件都存在SynchronizationContext.Currentnull的问题,异步延续在线程池上执行。我会在第一个SynchronizationContext.Current之前检查await的值。

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