【发布时间】:2013-04-03 03:37:14
【问题描述】:
我正在为 excel 创建一个 VSTO 功能区插件,并且我在我的应用程序中存储了一些工作簿状态信息,用于更新已启用的可视按钮。考虑到可能有多个工作簿,我将此状态对象存储在 ThisAddIn 类的字典中。我的问题是我不知道如何为工作簿获取唯一的哈希/密钥/Guid,因为我得到的只是一个不断更改哈希的 COM 包装器。很公平,我完全理解这一点。
我使用了很长时间的一个解决方案是创建一个 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 对象”。
这是引用 Workbook COM 对象的安全方式吗? 如果我有两个功能区应用程序都使用此方法来获取单个工作簿 GUID,该怎么办? 如果其中一个应用程序在我的状态对象上运行垃圾收集器,它调用终结器来调用 Marshal.FinalReleaseComObject(workbook),该怎么办? 有什么方法可以获取工作簿的引用计数,这样我就不会在其他功能区应用程序完成之前调用 FinalRelease? 清理 VSTO 中的 Workbook COM 对象以保持与这些其他应用公平竞争的最佳做法是什么?
当然,我不是第一个希望根据工作簿状态启用按钮的人,其他人是如何做到这一点的?我在 Stack Overflow 上查看了其他几篇文章,但没有一篇对 Workbook Guid 解决方案有帮助。
我正在使用功能区设计器,并连接到工作簿加载和停用事件。
提前致谢,希望我提供了所有详细信息。
【问题讨论】:
-
无法使用与底层 RCW 分离的 COM 对象 - 是的,我讨厌这个错误,它是一个 PITA,请阅读:jake.ginnivan.net/vsto-com-interop
-
为什么在同一个函数中释放IUnknown指针?尝试保留它并仅在工作簿关闭或类似情况时释放它。
-
太棒了,谢谢@JeremyThompson!从那篇文章中可以学到很多东西。 VSTOContrib 看起来很适合我的下一个项目,但它并不能解决我眼前的问题。 @SimonMourier - 我对本机代码不是很了解,但我想如果我不在这里释放指针,那么我会忘记它以便稍后发布?