当我使用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调用之前
为什么会发生这种情况?可能的解决方法是什么?
答案 0 :(得分:1)
因为在这种情况下Task
可能会启动一个不是STA线程的新线程。您对updateDoc
和newDoc
的调用是调用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)