来自UI线程的Task.Run抛出STA错误

时间:2016-12-09 07:28:50

标签: c# multithreading office-interop

当我使用Office.Interop库重构一些旧的C#代码来生成文档时,我发现了这一点,因为当它调用函数时它正在使用UI上下文阻止它

实施例

private void btnFooClick(object sender, EventArgs e)
{
      bool documentGenerated = chckBox.Checked ? updateDoc() : newDoc();

      if(documentGenerated){
        //do something
      }
}

决定改变它,以减少阻止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
      }
}

它抛出了这样的错误

  

当前线程必须设置为单线程单元(STA)模式   在可以进行OLE调用之前

为什么会发生这种情况?可能的解决方法是什么?

2 个答案:

答案 0 :(得分:1)

因为在这种情况下Task可能会启动一个不是STA线程的新线程。您对updateDocnewDoc的调用是调用Interop层的调用,它与MTA线程不同。

您可以重构此操作以使用Thread而不是Task,并自行将公寓设置为STA。我会小心,因为我不确定Interop是否喜欢多线程。

答案 1 :(得分:0)

通过Interop访问的COM组件要求调用线程是STA线程,但在您的情况下,它不是STA。使用STA可以通过多个线程访问组件。您可以在Understanding and Using COM Threading Models中详细了解需要STA的原因。您可以按照此answer中的建议在Task类上创建一个扩展方法,以通过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;
}

您也可以使用Thread代替任务,您必须将ApartmentState设置为STA,如thread.SetApartmentState(ApartmentState.STA)