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

本文关键字:于状态 按钮 状态 启用 VSTO 获取 Excel Hashcode 工作簿 | 更新日期: 2023-09-27 18:08:29

我正在为excel创建一个VSTO Ribbon插件,并且我正在我的应用程序中存储一些工作簿状态信息,我使用这些信息来更新可视化按钮启用。考虑到可以有多个工作簿,我将这个状态对象存储在ThisAddIn类的字典中。我的问题是,我不知道如何获得一个唯一的哈希/键/Guid的工作簿,因为所有我得到的是一个COM包装器,不断地改变哈希。有道理,我完全理解。

我使用了很长一段时间的一个解决方案是创建一个向导并将其存储在工作簿的CustomDocumentProperties中,并将基于该向导的状态映射为键。这至少可以工作,但如果我创建一个工作簿的副本并在同一应用程序实例中打开它,并且现在有多个具有相同向导的工作簿,则会失败。

我现在有了一个想法,我想我可以在Workbook_Open事件上刷新这个指南。但这似乎仍然是一个狡猾的解决方案。

我在这里找到的第二个解决方案:http://social.msdn.microsoft.com/forums/en us/vsto/thread/04efa74d - 83 bd - 434 d - ab07 - 36742 - fd8410e/

所以我用那个家伙的代码创建了这个:
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);
        }
    }
}

它在几分钟内工作得很好,直到它开始给我臭名昭著的错误"COM对象已与其底层RCW分离,无法使用",当访问Application.ActiveWorkbook时。

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

当然我不是第一个想要基于工作簿状态启用按钮的人,其他人如何做到这一点?我在这里看了一些关于Stack Overflow的其他文章,但是没有一篇对我的Workbook Guid解决方案很有帮助。

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

提前感谢,希望我已经包括了所有的细节。

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

我最终通过简单地将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);
            }
        }
    }
}

然后在我的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;
    }
}

当然,现在我可以通过调用ThisAddIn.WorkbookState

从ribbon应用程序的任何地方访问我的WorkbookState

您也可以使用workbook.Application.Hwnd但这只会帮助你,如果你使用Excel 2013/2016,打开每个工作簿的新窗口。

Excel 2010打开同一窗口下的所有工作簿。