清理 CommandBar 按钮

Cleaning up CommandBar buttons

我的 COM 加载项有一个问题已经拖了几个月了,我不知道为什么。

IDTExtensibility2 实施已经由 Carlos Quintero(MZ-Tools 背后的人)进行了同行评审,并被认为是正确的。

根据他的建议,OnBeginShutdown 实现设置了一个在 OnDisconnection 中检查的标志,以确保 ShutdownAddIn 仅 运行 一次(某些 VBE 主机应用程序不调用 OnBeginShutdown,这就是为什么):

public void OnBeginShutdown(ref Array custom)
{
    _isBeginShutdownExecuted = true;
    ShutdownAddIn();
}

我的加载项使用 Ninject 作为 DI/IoC,而我的 ShutdownAddIn 方法归结为在 Ninject [=20= 上调用 Dispose ] 实例,然后用 Marshal.ReleaseComObject:

释放所有 COM 对象
private void ShutdownAddIn()
{
    if (_kernel != null)
    {
        _kernel.Dispose();
        _kernel = null;
    }
    _ide.Release();
    _isInitialized = false;
}

我想不出更早的时间 运行 这段代码。然而,当我的命令栏和菜单包装器上出现 Dispose 运行 时,当 commandbar/menus 试图拆除它们的控件时,我在 StopEvents 中得到一个 InvalidCastException

public void HandleEvents()
{
    // register the unmanaged click events
    ((Microsoft.Office.Core.CommandBarButton)Target).Click += Target_Click;
}

public void StopEvents()
{
    // unregister the unmanaged click events
    ((Microsoft.Office.Core.CommandBarButton)Target).Click -= Target_Click;
}

public event EventHandler<CommandBarButtonClickEventArgs> Click;
private void Target_Click(Microsoft.Office.Core.CommandBarButton ctrl, ref bool cancelDefault)
{
    // handle the unmanaged click events and fire a managed event for managed code to handle
    var handler = Click;
    if (handler == null)
    {
        return;
    }
    var args = new CommandBarButtonClickEventArgs(new CommandBarButton(ctrl));
    handler.Invoke(this, args);
    cancelDefault = args.Cancel;
}

InvalidCastException 说它不能转换为 IConnectionPoint - 我发现这是因为当这段代码 运行s 时,我的Target(包装的 __ComObject)已经消失了,我留下了一个无效的指针和对不再存在的 COM 对象的挥之不去的引用。

如果我在拆卸过程中捕捉到所有抛出的异常(当我尝试 Delete 按钮和菜单时,我有更多的异常源于相同的根本问题),主机应用程序关闭但主机进程仍然存在 - 然后我必须从 任务管理器 中将其杀死。我认为这种行为与由未删除的点击处理程序引起的内存泄漏一致。


是否有更强大的方法可以处理 Microsoft.Office.Core.CommandBarButton 包装器的 adding/removing 事件处理程序?为什么我包装的 COM 对象在 OnBeginShutdown 运行 时已经 "gone",如果我还没有释放它们?

我可能错了,但我不认为 InvalidCastException 是因为某些 COM 对象消失了,在那种情况下您会收到“无法使用与其底层 RCW 分离的 COM 对象”。 InvalidCastException 意味着它的意思,一个类型不能转换为另一种类型,这不仅会发生在类型全名不同的明显情况下,而且我在诸如

之类的边缘情况下也会发生

1) 类型全名相同,但来自不同的程序集,甚至来自以某种方式从不同位置加载两次的同一程序集。示例:Isolating .NET-based add-ins for the VBA editor with COM Shims

中提到的案例1

2) 类型全名相同但已在同一进程中加载​​到不同的 CLR (2.0 / 4.0) 中。示例:The strange case of System.InvalidCastException (“Unable to cast COM object of type ‘System.__ComObject’ to class type System.Windows.Forms.UserControl”) showing toolwindow

我建议获取正在转换的类型的完整类型 name/assembly names/CLR。添加对 Microsoft.VisualBasic 引用的临时引用允许您使用 Microsoft.VisualBasic.Information.TypeName(object) 获取 __ComObject.

背后的实际类型