在后台运行时发生Excel互操作COM异常
本文关键字:互操作 COM 异常 Excel 后台 运行时 | 更新日期: 2023-09-27 18:26:21
我正在尝试将一些样式应用于工作簿中的单元格。我想在后台线程中完成这项工作,这样我的GUI就可以保持响应。这项工作应该需要几秒钟的时间,如果我点击文档中的某个随机单元格,我会得到一个异常。这是我的代码:
public void ApplyStyles()
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += DoWork;
bw.RunWorkerAsync();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
try
{
foreach (ICell xcell in cells)
{
Microsoft.Office.Interop.Excel.Range cell = cellUtility.GetCell(xcell);
if (styles.ContainsKey(styleIds[xcell.Style]))
{
Style s = styles[xcell.Style];
cell.Style = s;
}
}
}
catch (Exception ex)
{
if (Logger.IsErrorEnabled)
{
Logger.Error(ex.ToString());
}
messageBox.ShowErrorMessage(localizationMessages.ApplyingErrorText, localizationMessages.ApplyingErrorCaption);
}
}
当异常发生时,这是我收到的消息;
System.Runtime.InteropServices.COMException (0x800AC472): Exception from HRESULT: 0x800AC472
at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData& msgData)
at Microsoft.Office.Interop.Excel.Range.set_Style(Object value)
at ABZ.ReportFactory.OfficeAddin.Excel.BatchLinking.BackgroundStyleApplier.DoWork() in C:'ABZ'ABZ ReportFactory Office Addin'ABZ.ReportFactory.OfficeAddin.Excel'BatchLinking'BackgroundStyleApplier.cs:line 86
是否可以在后台线程中执行这种样式应用操作?我该怎么做?
我遇到的第二个问题是,当这个样式应用程序在后台运行时,我的光标不断地从繁忙状态变为正常状态,直到这个操作结束。我希望光标是正常的。该用户完全不知道该后台操作。
欢呼,Vladimir
您必须确保从执行工作的线程中获取根Excel应用程序对象,并从中获取Range。ExcelCOM对象都位于单线程单元(STA)中,因此您不能仅从其他线程使用它们。
您没有显示"cellUtility.GetCell"实际上是如何获取Range的,但可能的问题是您使用的是最初从另一个线程检索到的Application对象。
在"后台线程上做这种工作,这样你的GUI就可以保持响应"是有问题的——Excel中的所有工作最终都发生在主Excel线程上,因为Excel(本质上)是单线程的。
有一些方法可以解决这个问题:
-
通常你会发现,关闭ScreenUpdating并将Calculation设置为Manual可以让你更快地完成编辑工作,然后你就不需要其他线程或任何东西了。
-
在主线程上运行您的工作,但将其分成小块,然后在向Excel屈服后安排下一块工作-您可以创建Windows.Forms.Timer,或者如果您可以运行Excel宏,请使用Application.OnTime来安排下一项工作。
-
如果您想从另一个线程执行工作,则需要在该线程上获取Application对象和其他COM对象。获取正确的Excel应用程序实例可能很困难,但Andrew Whitechapel在这里描述了一个很好的方法:http://blogs.officezealot.com/whitechapel/archive/2005/04/10/4514.aspx.然而,您仍然必须检查上来自另一个线程的每个COM调用上的错误,因为Excel可能很忙,并且可能随时拒绝来自另一线程的任何COM调用(特别是当您的用户正在与"响应GUI"交互时)。您至少需要检查带有错误的COMException-每个COM调用都可能引发以下其中之一:
- const uint RPC_E_SERVERCALL_RETRYLATER=0x8001010A
- const uint VBA_E_IGNORE=0x800AC472
ExcelDNA(我开发的Excel/.NET集成库)通过调用ExcelDnaUtil.Application,可以访问您正在运行的线程上的应用程序对象。但我仍然建议将与Excel对象模型的所有交互移动到主Excel线程,也许可以使用对应用程序的调用。Run to Run告诉Excel运行宏。然后Application.Run调用就变成了一个单点,在这里可以从后台线程检查并重试COMException。