如何修复 UI 线程中的 Task.Run 抛出 STA 错误

Dan*_*man 3 c# multithreading task office-interop

当我重构一些旧的 C# 代码以使用该库生成文档时Office.Interop,我发现了这一点,因为它使用了 UI 上下文。当从它调用函数时它会阻塞它

例如:

private void btnFooClick(object sender, EventArgs e)
{
      bool documentGenerated = chckBox.Checked ? updateDoc() : newDoc();
      
      if(documentGenerated){
        //do something
      }
}
Run Code Online (Sandbox Code Playgroud)

我决定更改它以减少阻塞 UI:

private async void btnFooClick(object sender, EventArgs e)
{
      bool documentGenerated; = chckBox.Checked ? updateDoc() : newDoc();
     
      if(chckBox.Checked)
      {
                documentGenerated = await Task.Run(() => updateDoc()).ConfigureAwait(false);
      }
      else
      {
                documentGenerated = await Task.Run(() => newDoc()).ConfigureAwait(false);
      }

      if(documentGenerated){
        //do something
      }
}
Run Code Online (Sandbox Code Playgroud)

它抛出了这个错误:

Current thread must be set to single thread apartment (STA) mode
before OLE calls can be made
Run Code Online (Sandbox Code Playgroud)

为什么会发生这种情况以及解决方法是什么?

Adi*_*dil 5

通过 Interop 访问的 COM 组件要求调用线程是 STA 线程,但在您的情况下它不是 STA。否则,可以通过多个线程访问 STA 组件。您可以在了解和使用 COM 线程模型中阅读有关为什么需要 STA 的更多信息。

您可以按照在任务上设置 ApartmentState中的建议在任务类上创建扩展方法,以使用任务通过 Interop 调用 COM 组件:

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;
}
Run Code Online (Sandbox Code Playgroud)

当您使用Thread而不是task时,您必须使用类似的方法将ApartmentState设置为STA thread.SetApartmentState(ApartmentState.STA)