2013-04-11 102 views
5

我創建一個VSTO絲帶外接程序爲Excel,和我存儲我的應用程序工作簿的一些狀態信息,我用它來更新啓用視覺按鈕啓用按鈕。考慮到可以有多個工作簿,我將這個狀態對象存儲在ThisAddIn類的字典中。我的問題是,我不知道如何獲得工作簿的唯一哈希/鍵/ Guid,因爲我所得到的是一個不斷更改哈希的COM包裝器。夠公平的,我完全明白這一點。我用了很長時間獲取的哈希碼Excel工作簿中VSTO基於狀態

一種解決方案是創建一個GUID,並將其存儲在CustomDocumentProperties工作簿,並根據,作爲一個關鍵的狀態映射。這至少可以工作,但如果我創建工作簿的副本並在相同的應用程序實例中打開該工作簿,並且現在具有多個具有相同GUID的工作簿,則失敗。

我剛剛有一個想法,現在我想我可以在Workbook_Open事件中刷新此Guid。但是,這仍然是一個狡猾的解決方案。

第二個解決方案,我發現在這裏: http://social.msdn.microsoft.com/Forums/en-US/vsto/thread/04efa74d-83bd-434d-ab07-36742fd8410e/

所以我使用的球員代碼和創造了這個:

public static class WorkbookExtensions 
{ 
    public static IntPtr GetHashery(this msExcel.Workbook workbook) 
    { 
     IntPtr punk = IntPtr.Zero; 
     try 
     { 
      punk = Marshal.GetIUnknownForObject(workbook); 
      return punk; 
     } 
     finally 
     { 
      //Release to decrease ref count 
      Marshal.Release(punk); 
     } 
    } 
} 

它非常好幾分鐘,直到它開始給我臭名昭著錯誤「訪問Application.ActiveWorkbook時,不能使用與其基礎RCW分離的COM對象」。

這是引用該工作簿COM對象的一種安全的方法? 如果我有兩個使用此方法獲取單個工作簿GUID的功能區應用程序,該怎麼辦? 如果這些應用程序的一個運行我的國家,對象的垃圾收集器,它要求一個終結調用Marshal.FinalReleaseComObject(簿)什麼? 有沒有什麼辦法可以獲得工作簿的參考計數,以便在其他功能區應用程序完成之前不調用FinalRelease? 清理VSTO中的Workbook COM對象以保持與其他應用程序公平對待的一些最佳做法是什麼?

當然我不是第一人,希望有按鈕啓用基於工作簿狀態,怎麼每個人都這樣做呢?我在Stack Overflow上看過一些其他文章,但沒有一篇能夠幫助我解決Workbook Guid解決方案。

我正在使用功能區設計器,並連接到工作簿加載和停用事件。提前

感謝,希望香港專業教育學院包括所有的細節。

+1

**已從其基礎R分離的COM對象CW無法使用** - 是的,我討厭這個錯誤它的PITA,閱讀此:http://jake.ginnivan.net/vsto-com-interop – 2013-04-11 03:53:37

+1

爲什麼你釋放IUnknown指針在相同的功能?只有在工作簿已關閉或類似的情況下,才能保留並釋放它。 – 2013-04-11 05:38:39

+0

輝煌,謝謝@JeremyThompson!這篇文章有很多要學習。 VSTOContrib看起來對我的下一個項目來說很好,但它並不能解決我眼前的問題。 @SimonMourier - 我對本地代碼不是很瞭解,但是我想如果我不在這裏釋放指針,那麼我會失去它以後再發布的軌跡? – stuzor 2013-04-11 16:25:19

回答

5

我最終解決了這個問題,只需將IntPtr強制轉換爲長整型,然後IntPtr的處置就不會影響到我。我不需要保存IntPtr,因爲我真正需要的是對工作簿唯一的東西。

以下代碼允許我存儲特定於工作簿的狀態信息,以便可以基於自定義對象工作簿狀態更新我的功能區中按鈕的可視狀態。您可以在自定義WorkbookState類中存儲您想要的任何信息,但通常它會是您不希望在電子表格中保留的會話特定信息。

單獨的工作簿擴展:

public static class WorkbookExtensions 
{ 
    public static long GetHashery(this msExcel.Workbook workbook) 
    { 
     if (workbook == null) 
     { 
      throw new ArgumentNullException("workbook"); 
     } 

     IntPtr pUnknown = IntPtr.Zero; 
     try 
     { 
      pUnknown = Marshal.GetIUnknownForObject(workbook); 
      return pUnknown.ToInt64(); 
     } 
     finally 
     { 
      // GetIUnknownForObject causes AddRef. 
      if (pUnknown != IntPtr.Zero) 
      { 
       Marshal.Release(pUnknown); 
      } 
     } 
    } 
} 

然後在我的VSTO/ExcelDna ThisAddIn類我存儲地圖上面的方法都工作簿狀態,找到一個獨特的工作簿中的哈希鍵:

private Dictionary<long, WorkbookState> _workbookStates = new Dictionary<long, WorkbookState>(); 
public WorkbookState WorkbookState 
{ 
    get 
    { 
     long hash = Application.ActiveWorkbook.GetHashery(); 
     WorkbookState state; 
     if (!_workbookStates.TryGetValue(hash, out state)) 
     { 
      state = _workbookStates[hash] = new WorkbookState(); 
     } 
     return state; 
    } 
} 

當然,現在我可以從我的功能區應用程序中的任何位置訪問我的WorkbookState,只需撥打ThisAddIn.WorkbookState

+1

這從Task.Run調用時爲ActiveWorkbook提供了另一個散列。 – smg 2016-03-16 07:08:36

+0

我從調用表單和色帶的哈希方法得到兩個不同的長度 – 2016-10-24 19:23:19

+0

嗯,你提出好點@ChrisHayes和user3009578。我從那以後就知道ActiveWorkbook只能從UI線程中調用。由於ActiveWorkbook可以改變,所以從UI線程以外的任何線程引用它都是一個糟糕的策略。因此,我發現最好的策略是將ActiveWorkbook緩存在用戶操作事件處理程序中,並將ActiveWorkbook用於所有線程。如果後臺線程需要在沒有用戶操作的情況下訪問ActiveWorkbook,請通過監聽Application.ActiveWorkbookChanged事件來緩存自己的副本。無需downvote^_ ^ – stuzor 2016-10-25 04:36:44