我的 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 IKernel
实例上调用 Dispose
,然后使用 Marshal.ReleaseComObject
释放所有 COM 对象:
private void ShutdownAddIn()
{
if (_kernel != null)
{
_kernel.Dispose();
_kernel = null;
}
_ide.Release();
_isInitialized = false;
}
我想不出更早的时间来运行这段代码。然而,当 Dispose
在我的命令栏和菜单包装器上运行时,当命令栏/菜单试图拆除它们时,我在 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
- 我发现这是因为当这段代码运行时,我的 Target
(包装的 __ComObject
)已经消失了,我只剩下一个无效的指针和一个对不再存在的 COM 对象的延迟引用。
如果我在拆卸过程中捕捉到所有抛出的异常(当我尝试 Delete
按钮和菜单时,我有更多的异常源于相同的根本问题),主机应用程序关闭但主机进程仍然存在 - 然后我必须从任务管理器中将其终止。我认为这种行为与由未删除的点击处理程序引起的内存泄漏一致。
是否有更可靠的方法来处理为 Microsoft.Office.Core.CommandBarButton
包装器添加/删除事件处理程序?如果我还没有释放它们,为什么当 OnBeginShutdown
运行时我包装的 COM 对象已经“消失”了?
最佳答案
我可能是错的,但我不认为 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
我建议获取正在转换的类型的完整类型名称/程序集名称/CLR。添加对 Microsoft.VisualBasic 引用的临时引用允许您使用 Microsoft.VisualBasic.Information.TypeName(object) 获取 __ComObject 背后的实际类型。
关于c# - 清理 CommandBar 按钮,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40896893/