在VSTO中获取Excel工作簿的Hashcode以启用基于状态的button

我正在创build一个VSTOfunction区AddIn for Excel,并且在我的应用程序中存储了一些工作簿状态信息,用于更新可视化button。 考虑到可以有多个工作簿,我将这个状态对象存储在ThisAddIn类的字典中。 我的问题是,我不知道如何得到一个独特的哈希/键/ Guid的工作簿,因为我得到的是一个COM包装不断改变散列。 够公平的,我完全明白这一点。

我已经使用了很长时间的一个解决scheme是创build一个GUID并将其存储在工作簿的CustomDocumentProperties中,并将基于此的状态映射为键。 这至less工作,但它失败,如果我创build工作簿的副本,并在相同的应用程序实例中打开,并具有相同的GUID多个工作簿。

现在我只是有一个想法,我想我可以在Workbook_Open事件上刷新这个Guid。 但是,这仍然是一个狡猾的解决scheme。

我在这里find第二个解决scheme: http : //social.msdn.microsoft.com/Forums/en-US/vsto/thread/04efa74d-83bd-434d-ab07-36742fd8410e/

所以我用这个家伙的代码并创build了这个:

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对象的安全方法吗? 如果我有两个function区应用程序都使用此方法来获取单个工作簿GUID? 如果其中一个应用程序在我的状态对象上运行垃圾收集器,它会调用一个终结器来调用Marshal.FinalReleaseComObject(workbook)? 有什么办法可以获得工作簿的Ref Count,以便在其他function区应用程序完成之前不要致电FinalRelease? 清理VSTO中的Workbook COM对象以保持与其他应用程序公平对待的最佳做法是什么?

当然,我不是第一个想要基于工作簿状态启用button的人,其他人怎么做呢? 我已经在Stack Overflow上看了一些其他的文章,但是没有一个能够帮助我解决Workbook Guid问题。

我正在使用function区devise器,并连接到“工作簿加载”和“停用”事件。

在此先感谢,希望包括所有的细节。

我最终通过简单地将IntPtr强制转换为long来解决这个问题,然后IntPtr的处理不会影响到我。 我不需要保留IntPtr,因为我真正需要的是对于工作簿唯一的东西。

以下代码允许我存储特定于工作簿的状态信息,以便可以基于自定义对象工作簿状态更新我的function区中button的可视状态。 您可以将任何想要的信息存储在自定义的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类中,我用上面的方法存储了所有工作簿状态的映射,以find一个唯一的工作簿散列键:

 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; } } 

当然,现在只需调用ThisAddIn.WorkbookState ,就可以从我的function区应用程序的任何位置访问我的WorkbookState