在后台运行时Excel互操作COM异常

时间:2012-03-01 11:31:03

标签: c# office-interop excel-interop

我正在尝试将一些样式应用于工作簿中的单元格。我想在后台线程中执行此操作,以便我的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

是否可以在后台线程中执行此样式应用操作?我应该怎么做?

我遇到的第二个问题是,当这个样式应用在后台运行时,我的光标会不断地将状态从忙碌变为常规,直到此操作结束。我想光标是正常的。该用户完全没有意识到这种后台操作。

欢呼声, 弗拉基米尔

1 个答案:

答案 0 :(得分:13)

您必须确保从正在完成工作的线程中获取根Excel Excel应用程序对象,并从那里获取Range。 Excel COM对象都存在于单线程单元(STA)中,因此您不能仅从其他线程中使用它们。

您没有显示“cellUtility.GetCell”如何实际获取Range,但可能的问题是您正在使用最初从另一个线程检索的Application对象。

在“后台线程上执行此类工作,因此您的GUI可以保持响应”是有问题的 - Excel中的所有工作最终都发生在主Excel线程上,因为Excel(基本上)是单线程的。

有一些方法可以解决这个问题:

  1. 通常您会发现关闭ScreenUpdating并将“计算”设置为“手动”可以让您更快地完成编辑工作,然后您就不需要其他线程或其他任何线程。

  2. 在主线程上运行你的工作,但是把它分成小块,然后在屈服于Excel之后安排下一个块完成 - 你可以创建一个Windows.Forms.Timer或者你可以运行一个Excel宏使用Application.OnTime来安排下一部分工作。

  3. 如果要从另一个线程执行工作,则需要在该线程上获取Application对象和更多COM对象。获得正确的Excel应用程序实例可能很棘手,但Andrew Whitechapel在此处描述了一个很好的方法:http://blogs.officezealot.com/whitechapel/archive/2005/04/10/4514.aspx。但是,您仍然需要检查来自另一个线程的每个COM调用的错误,因为Excel可能很忙并且可能随时拒绝来自另一个线程的任何COM调用(特别是如果您的用户正在与之交互)那个'响应式GUI'。你需要至少检查错误的COMExceptions - 每个COM调用都可以抛出其中一个:

    • const uint RPC_E_SERVERCALL_RETRYLATER = 0x8001010A;
    • const uint VBA_E_IGNORE = 0x800AC472;
  4. Excel-DNA(我开发的Excel / .NET集成库)通过调用ExcelDnaUtil.Application来访问正在运行的线程上的Application对象。但我仍然建议将所有与Excel对象模型的交互移动到主Excel线程,也许使用对Application.Run的调用来运行告诉Excel运行宏。然后,Application.Run调用成为一个单点,可以从后台线程检查和重试COMException。