我有一个问题,我的COM插件已经拖了几个月,我不知道为什么。
IDTExtensibility2实现已经被Carlos (MZ-Tools背后的家伙)同行评审过,并且被认为是正确的。
根据他的建议,OnBeginShutdown实现设置了一个在OnDisconnection中签入的标志,以确保ShutdownAddIn只运行一次(有些ShutdownAddIn主机应用程序不调用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按钮和菜单时,有更多的来自同一根问题的异常),主机应用程序将关闭,但是主机进程仍然存在--然后我必须从Task中杀死它。这种行为与未删除的单击处理程序造成的内存泄漏是一致的。
是否有更健壮的方法来处理为Microsoft.Office.Core.CommandBarButton包装器添加/删除事件处理程序的问题?如果我还没有发布OnBeginShutdown,为什么我的包装COM对象在运行时已经“消失”了呢?
发布于 2016-11-30 22:06:13
我可能错了,但我不认为InvalidCastException是因为一些COM对象消失了,在这种情况下,您将收到“已与其底层RCW分离的COM对象”。InvalidCastException的意思是,一种类型不能转换为另一种类型,这种情况不仅在类型全名不同的明显情况下发生,而且我在边缘情况下也看到过,例如
1)类型全名是相同的,但来自不同的程序集,甚至来自不同位置加载两次的同一程序集。示例:使用COM Shims为VBA编辑器隔离基于.NET的外接程序中提到的案例1
2)类型全名相同,但在同一进程中加载在不同的CLR (2.0 / 4.0)中。示例:ComObject‘to class type System.Windows.Forms.UserControl“)显示工具窗口
我建议获取正在转换的类型的完整类型名称/程序集名称/CLR。向Microsoft.VisualBasic.Information.TypeName(object)引用添加临时引用允许您使用Microsoft.VisualBasic获取__ComObject后面的实际类型。
https://stackoverflow.com/questions/40896893
复制相似问题