2012-03-01 106 views
1

我正在尝试将一些样式应用到工作簿中的单元格。我想在后台线程中做到这一点,所以我的GUI可以保持响应。这项工作应该需要几秒钟,如果我点击文档中的一些随机单元格,我会得到一个异常。这里是我的代码:在后台运行时Excel互操作COM异常

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 

是否有可能做这种风格在后台线程应用操作?我该怎么做?

我遇到的第二个问题是,当这种风格的应用程序在后台运行时,我的光标不断地从繁忙状态变为常规状态,直到此操作结束。我希望光标正常。该用户完全不知道这种背景操作。

欢呼声, 弗拉基米尔

+0

http://www.myomron.com/index.php?action=kb&article=1324 – 2012-03-01 12:29:04

+0

因此,Excel对象是可见的,而这是发生? – SeanCocteau 2012-03-01 12:38:09

回答

12

你必须确保你得到根本Excel应用程序对象,并从那里的范围,从线程那里的工作正在做。 Excel COM对象全部位于单线程公寓(STA)中,因此您不能仅从其他线程使用它们。

您不显示“cellUtility.GetCell”实际上如何获取Range,但可能的问题是您正在使用最初从另一个线程检索到的Application对象。因为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调用可以抛出下列操作之一:

    • 常量UINT RPC_E_SERVERCALL_RETRYLATER = 0x8001010A;
    • 常量UINT VBA_E_IGNORE = 0x800AC472;

Excel_DNA(Excel /。我开发的.NET集成库)允许通过调用ExcelDnaUtil.Application访问正在运行的线程上的Application对象。但我仍然建议将与Excel对象模型的所有交互移动到主Excel线程,可能使用对Application.Run的调用来运行tell Excel以运行宏。然后,Application.Run调用成为一个单点,COMException可以从后台线程中检查并重试。

+0

对于我来说,从另一个线程创建应用程序实例似乎是不可能的,因为我使用DI,因此每个进程只有一个实例。我可能会忽略那个例外,并尝试重复操作,直到它结束。 – Vajda 2012-03-01 15:43:28

+0

难道你不能'依赖注入'一个帮助对象与一个方法来检索它正在运行的线程上的应用程序对象吗? 如果您确实试图通过错误线程上的STA COM对象与Excel对象模型交谈,则表明您处于不受支持的配置。即使您在正确的线程中获得应用程序,仍然需要检查并处理这些错误。 – Govert 2012-03-01 16:54:14