在C#中释放COM对象

8

我知道之前已经讨论过这个问题,但是我没有找到一个令人满意的答案。

我有一个电子邮件文件(.msg),我按照下面的方式打开它,然后调用Display函数。

oApp = new Microsoft.Office.Interop.Outlook.Application();
mail = (Microsoft.Office.Interop.Outlook.MailItem)oApp.Session.OpenSharedItem(fileName);
mail.Display(false);
oApp = null; // do I need to release this object ?

用户可以关闭窗口并重新打开它。在他们点击“重新打开”之前,我会检查该窗口是否仍然存在,如果存在,则只需向该窗口发送SetForeground(hwnd)信号。 如果不存在,则意味着用户已关闭它,因此只需释放mailItem对象并重新打开。

 public static void ReleaseCOMObject(Microsoft.Office.Interop.Outlook.MailItem item) {
        int r = System.Runtime.InteropServices.Marshal.ReleaseComObject(item);
        while (r != 0) {
            r = System.Runtime.InteropServices.Marshal.ReleaseComObject(item);
        }
    }

如果我尝试再次打开同一个文件,它会“有时”抛出“文件正在使用异常”,因此,我认为即使我释放了邮件项,它也没有被正确释放。
我该怎么做才能确保它被正确释放。关闭并重新打开文件是非常常见的情况。
任何指针将非常有帮助。

1
只有在增加引用计数时才应该进行“释放”,释放其他人的引用可能会导致问题。 - Matthew
2个回答

8
如果你正在使用.NET的COM-interop功能(你正在使用),那么你不需要担心这个问题。
COM跟踪引用计数 - 当引用计数达到0时,COM对象会自动释放 - .NET会为你处理标准COM引用计数机制的工作。
如果你在向C库执行P/Invoke操作,情况可能会有所不同 - 但在像你这样的标准场景中,你不应该有任何担心。

不确定我做错了什么。我注意到一旦出现“文件正在使用异常”,重新启动我的Outlook就可以解决问题。对于“oApp = new Microsoft.Office.Interop.Outlook.Application();”,我需要进行哪种清理? - karephul
3
现在它正在运作 -> 还有一个模块一直在持有该邮件项且未释放。 - karephul
顺便说一下,我想让你知道我仍然需要“ReleaseCOMObject”才能使它正常工作。 - karephul
@karephul - 我不反对 Nanhydrin 的观点,即“尽早处理,经常处理”始终是一个好主意,无论情况如何。如果某个对象是 IDisposable 类型,它应该被包装在 using 语句中或在可释放的类中使用,并在主类被释放时进行处理。然而,在特殊情况下,你不必手动调用 ReleaseCOMObject。http://blogs.msdn.com/b/visualstudio/archive/2010/03/01/marshal-releasecomobject-considered-dangerous.aspx - Steve

3

做类似这样的事情:

mail = (Microsoft.Office.Interop.Outlook.MailItem)oApp.Session.OpenSharedItem(fileName);

如果您没有正确地处理子对象的引用,则即使调用ReleaseComObject,也会导致引用不被处置。

您应该按照以下方式进行调用:

session = oApp.Session;
mail = (Microsoft.Office.Interop.Outlook.MailItem)session.OpenSharedItem(fileName);

你需要依次处理每个子对象,如会话(session),并且进行处理。


我注意到如果我的电脑上没有运行Outlook..关闭并重新打开(我之前描述的方式)可以正常工作。我认为Outlook以某种方式持有对该文件的引用。有什么指针吗?我也尝试单独释放Session对象(不起作用)。 - karephul
你是在获取邮件项的同一方法中调用Marshal.ReleaseComObject方法吗?还是在一些onclose事件处理程序中进行操作? - Nanhydrin
在用户打开邮件项之前,我们会检查是否有相同标题的窗口。如果没有,则释放邮件项的 COM 对象并重新打开。[用户可能已经关闭了电子邮件窗口]..基本上我尽可能长时间地保留 MailItem,以便可以定期保存它。 - karephul
顺便问一下,我可以在不同的线程中释放 ReleaseComObject(item) 吗?比如在后台?我注意到即使 "ReleaseComObject" 能够释放对象,它也需要很长时间(**只有一个引用)。 - karephul
你不能依赖垃圾回收器在你处理完对象后立即将其清除,所以最好在使用完对象后立即处理掉它们。我认为你可以在任何地方释放COM对象,但个人而言,我更喜欢在使用它们的同一方法中释放COM对象,或者如果范围超出当前方法,则尽快释放它们,这样可以减少我错过对象并导致某些问题的可能性。 - Nanhydrin

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