如何恢复未能正确释放的COM对象?

3
当某人使用任务管理器结束实例化了COM对象的.NET进程时,据我所知,没有办法在try {} finally {}、using {}或事件处理块中调用Marshal.FinalReleaseComObject。下次启动应用程序时,COM对象处于不可用状态,我需要重新启动机器才能使其工作。
我使用Marshal.BindToMoniker来实例化对象。
我做错了什么?有什么我错过的东西吗?
我尝试了Saeed的建议,但它没有起作用。
private const int WM_CLOSE = 0x0010;

protected override void WndProc(ref Message m)
{
    if (m.Msg == WM_CLOSE)
    {
        logger.Info("WM_CLOSE");
        Marshal.FinalReleaseComObject(comObject);
    }

    base.WndProc(ref m);
}

1
我认为你可以钩取任务管理器的结束进程消息,然后使用marshal.release来防止错误释放,但是关于钩取消息,有一些vc++的文章,我不知道是否有c#的(无论如何,你可以用com将其包装以在c#中使用)。 - Saeed Amiri
@SaeedAmiri 我尝试了你的建议,但是它没有起作用,见下面我的答案。 - Jader Dias
1
我不认为WM_CLOSE是这个情况下合适的消息,也许你可以尝试其他消息,可能会得到结果(我已经很久没有处理相关的内容了,而且我的长期记忆力比较弱;)。但是如果你说它无法工作,是指在调试模式下无法进入程序吗? - Saeed Amiri
1
这些是哪种类型的COM对象?单例还是实例?进程内还是进程外?进程内的对象会随着您的进程一起销毁。进程外的对象将在约6分钟后清理。但是如果对象是实例,则旧对象无关紧要。 - Raymond Chen
1
当有人使用任务管理器来结束一个.NET进程时,你应该意识到这不是正常的关闭过程,因此如果之后产生异常行为,你不应感到惊讶。 - Damien_The_Unbeliever
显示剩余3条评论
2个回答

1

你可能不需要这样做。当垃圾回收器释放内存时,它会检查对象是否有一个未调用的Dispose()方法。如果Dispose()方法没有被调用,它将调用该方法。

我在控制台应用程序中进行了测试,并在我的Dispose函数中使用了3个扬声器蜂鸣声。在没有调用对象的Dispose的情况下退出应用程序后,大约需要30秒才能听到3个蜂鸣声。时间是不确定的。

当然,对象必须已经编写正确。

你是在关机后立即尝试运行应用程序吗?为什么不等几分钟再试试看呢?

编辑:我假设你的Dispose函数中有Marshal.FinalReleaseComObject。


当从IDisposable.Dispose方法调用记录器时,未发生任何事情,也没有将日志写入文件。 - Jader Dias

1

任务管理器正在使用本地等效的Process.Kill(或者可能是更强大的东西)。请注意文档中的以下叙述:

Kill 强制终止进程,而 CloseMainWindow 仅请求终止。

...

调用 CloseMainWindow 发送一个关闭主窗口的请求,这在一个良好形式的应用程序中...

并且,

如果您调用Kill,则进程编辑的数据或分配给进程的资源可能会丢失。 Kill 导致异常进程终止,应仅在必要时使用。

简而言之,从程序代码内部没有真正可以保护免受此影响的方法 - 插头已被拔出。不会发送任何窗口消息。


2
如果我没记错的话,任务管理器在用户在“进程”选项卡上点击“结束进程”时调用TerminateProcess来终止任务,并在“应用程序”选项卡上点击“结束任务”时通过WM_CLOSE消息来进行清理。此外, Raymond Chen的文章 解释了为什么无法停止TerminateProcess。 - Joshua
所以,如果我的解决方案存在,它必须在我从这样的崩溃中恢复对象的方式上,而不是在我处理突然终止的方式上。 - Jader Dias

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接