2012-03-01 74 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