2012-12-06 24 views
3

我想在Excel Addin上啓用IMessageFilter我必須寫入excel。 我已經採取了從here例子說:我怎樣才能讓C#定時器在創建它的同一個線程上執行?

消息過濾器是每個線程的,所以我們註冊這個線程作爲 消息過濾器(而不是主線程的加載項上創建 - 因爲這是Excel的主線程

我的問題是,我的系統寫入到Excel當計時器到期導致的寫作方法是從一個線程池線程打破IMessageFilter因爲Excel不能訪問的IMessageFilterRetryRejectedCall部分叫,因爲它生活在調用者線程而不是定時器產生的執行線程。

所以,我的問題是:有沒有一種方法可以強制定時器的Elapsed事件在初始化定時器的同一個線程上運行?

編輯:

我的問題是,如何讓我IMessageFilter擅長捕捉錯誤時,它拋出拒絕/忙什麼呢?

THX

+0

我認爲這很簡單,只需在循環中使用Thread.Sleep()和自己的條件即可。 – pylover

+0

謝謝。我想避免那樣旋轉。 –

回答

3

的完整的答案如下:

問題:類,要寫入數據excel無法處理來自excel的「忙/拒絕」響應消息。

解決方法:如描述here

IMessageFilter定義(自鏈接)實現IMessageFilter接口:我的課的

namespace ExcelAddinMessageFilter 
{ 
     [StructLayout(LayoutKind.Sequential, Pack = 4)] 
     public struct INTERFACEINFO 
     { 
      [MarshalAs(UnmanagedType.IUnknown)] 
      public object punk; 
      public Guid iid; 
      public ushort wMethod; 
     } 

     [ComImport, ComConversionLoss, InterfaceType((short)1), 
     Guid("00000016-0000-0000-C000-000000000046")] 
     public interface IMessageFilter 
     { 
      [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, 
       MethodCodeType = MethodCodeType.Runtime)] 
      int HandleInComingCall([In] uint dwCallType, [In] IntPtr htaskCaller, 
       [In] uint dwTickCount, 
       [In, MarshalAs(UnmanagedType.LPArray)] INTERFACEINFO[] 
       lpInterfaceInfo); 

      [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, 
       MethodCodeType = MethodCodeType.Runtime)] 
      int RetryRejectedCall([In] IntPtr htaskCallee, [In] uint dwTickCount, 
       [In] uint dwRejectType); 

      [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, 
       MethodCodeType = MethodCodeType.Runtime)] 
      int MessagePending([In] IntPtr htaskCallee, [In] uint dwTickCount, 
       [In] uint dwPendingType); 
     } 
    } 

IMessageFilter實現部分(見鏈接):

#region IMessageFilter Members 

     int ExcelAddinMessageFilter.IMessageFilter. 
      HandleInComingCall(uint dwCallType, IntPtr htaskCaller, uint dwTickCount, ExcelAddinMessageFilter.INTERFACEINFO[] lpInterfaceInfo) 
     { 
      // We're the client, so we won't get HandleInComingCall calls. 
      return 1; 
     } 

     int ExcelAddinMessageFilter.IMessageFilter. 
     RetryRejectedCall(IntPtr htaskCallee, uint dwTickCount, uint dwRejectType) 
     { 
      // The client will get RetryRejectedCall calls when the main Excel 
      // thread is blocked. We can handle this by attempting to retry 
      // the operation. This will continue to fail so long as Excel is 
      // blocked. 
      // As an alternative to simply retrying, we could put up 
      // a dialog telling the user to close the other dialog (and the 
      // new one) in order to continue - or to tell us if they want to 
      // abandon this call 
      // Expected return values: 
      // -1: The call should be canceled. COM then returns RPC_E_CALL_REJECTED from the original method call. 
      // Value >= 0 and <100: The call is to be retried immediately. 
      // Value >= 100: COM will wait for this many milliseconds and then retry the call. 
      return 1; 
     } 

     int ExcelAddinMessageFilter.IMessageFilter. 
      MessagePending(IntPtr htaskCallee, uint dwTickCount, uint dwPendingType) 
     { 
      return 1; 
     } 

     #endregion 

隨着IMessageFilter接口定義和實現,我設置了STA ThreadTimers.Timer如下:

螺紋:

thread = new Thread(WriteToExcel); 
thread.SetApartmentState(ApartmentState.STA); 

計時器:

timer = new System.Timers.Timer(); 
timer.Interval = 2000; 
timer.Elapsed += new ElapsedEventHandler(StartSTAThread_Handler); 

其中StartSTAThread_Handler定義爲:

void StartSTAThread_Handler(object source, ElapsedEventArgs e) 
{ 
    thread.Start(); 
    thread.Join(); 
    thread = null; 
} 

該線程調用我使用的方法寫入Excel並使用0123處理拒絕的消息上述接口。我必須做的最後一件事是完全符合OM的參考資格,也就是說;而不是:

Excel.Range rng = app.ActiveSheet.Range["range_name"]; 
rng.Copy(); // ERRROR: message filter's RetryRejectedCall is NOT called 

我不得不使用完全合格的引用:

app.ActiveSheet.Range["range_name"].Copy // OK: calls RetryRejectedCall when excel dialog etc is showing 

雖然這似乎爲我的工作需要,它似乎矛盾的另一個海報here描述的「二點規則」 ...

7

可以使用Timer.SynchronizingObject屬性編組時的間隔已經過去所發出的事件處理程序調用。

下面是MSDN

當經過事件是由視覺的Windows處理窗體部件, 如按鈕,通過該系統線程訪問該組件 池可能導致異常或只是可能無法工作。通過將SynchronizingObject設置爲Windows窗體組件, 可以避免這種 的影響,這會導致處理Elapsed事件的方法在 上與構建組件的線程相同。

假設使用的是WinFrom並正在從主模內創建定時器實例:

System.Timers.Timer t = new System.Timers.Timer(); 
t.SynchronizingObject = this; 
t.Elapsed += t_Elapsed; 
t.Start(); 
+1

感謝您的迴應。我沒有使用winform - 只是一個普通的C#類。關於最初的問題,我發現我間接指向excel的問題* my_range [「some_name」] .copy *而不是* excel_app.ActiveSheet.range [「some_name」] .copy *。這似乎違反了發佈COM對象的「雙點規則」,如下所述:http://stackoverflow.com/questions/158706/how-to-properly-clean-up-excel-interop-objects ... –

+1

你可能想在這裏分享您的解決方案 –

+0

當然;見下文。 –

相關問題