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

stu*_*zor 6 .net c# com excel vsto

我正在为excel创建一个VSTO功能区AddIn,我在我的应用程序中存储了一些工作簿状态信息,用于更新可视按钮已启用.考虑到可以存在多个工作簿,我将此状态对象存储在ThisAddIn类的字典中.我的问题是我不知道如何获得工作簿的唯一Hash/Key/Guid,因为我得到的是一个不断更改哈希值的COM包装器.很公平,我完全理解.

我已经使用了很长时间的一个解决方案是创建一个guid并将其存储在工作簿的CustomDocumentProperties中,并将基于该状态的状态映射为关键字.这至少有效,但如果我创建工作簿的副本并在同一个Application实例中打开它并且现在有多个工作簿具有相同的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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它运行得非常好几分钟,直到它开始给我一个臭名昭着的错误"COM对象已经与其底层RCW分离不能使用"访问Application.ActiveWorkbook.

这是一种引用Workbook COM对象的安全方法吗?如果我有两个功能区应用程序都使用此方法获取单个工作簿GUID怎么办?如果其中一个应用程序在我的状态对象上运行垃圾收集器,调用终结器来调用Marshal.FinalReleaseComObject(工作簿),该怎么办?有什么方法可以获得工作簿的Ref Count,这样我就不会在其他Ribbon Apps完成之前调用FinalRelease吗?在VSTO中清理Workbook COM对象以保持与其他应用程序公平竞争的最佳做法是什么?

当然,我不是第一个想要根据Workbook状态启用按钮的人,其他人如何做到这一点?我在Stack Overflow上看过其他几篇文章,但没有一篇能帮助我解决Workbook Guid解决方案.

我正在使用功能区设计器,并连接到工作簿加载和停用事件.

在此先感谢,希望我已经包括所有的细节.

stu*_*zor 6

我最终通过简单地将IntPtr转换为long来解决这个问题,然后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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在我的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,现在我可以通过简单的调用从我的功能区应用程序中的任何位置访问我的WorkbookState ThisAddIn.WorkbookState