Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 释放COM对象的正确方法?_C#_.net_Com - Fatal编程技术网

C# 释放COM对象的正确方法?

C# 释放COM对象的正确方法?,c#,.net,com,C#,.net,Com,有时,当我结束应用程序并尝试释放某些COM对象时,我会在调试器中收到警告: 检测到RaceOnRCWCleanUp 如果我编写了一个使用COM对象的类,我是否需要实现IDisposable并在IDisposable.Dispose中调用Marshal.FinalEleaseComObject 如果未手动调用Dispose,我是否仍需要在终结器中释放它们,或者GC是否会自动释放它们?现在我想知道这是否正确 我使用的COM对象还有一个类侦听的事件处理程序。显然,该事件是在另一个线程上引发的,因此,如

有时,当我结束应用程序并尝试释放某些COM对象时,我会在调试器中收到警告:

检测到
RaceOnRCWCleanUp

如果我编写了一个使用COM对象的类,我是否需要实现
IDisposable
并在
IDisposable.Dispose
中调用
Marshal.FinalEleaseComObject

如果未手动调用
Dispose
,我是否仍需要在终结器中释放它们,或者GC是否会自动释放它们?现在我想知道这是否正确


我使用的COM对象还有一个类侦听的事件处理程序。显然,该事件是在另一个线程上引发的,因此,如果在处理类时触发该事件,我如何正确处理它?

根据我使用不同COM对象(进程内或进程外)的经验,我建议每个COM/.NET边界交叉点使用一个
Marshal.ReleaseComObject
(如果您引用某个实例的COM对象以检索另一个COM引用)

我遇到了很多问题,只是因为我决定将COM互操作清理推迟到GC。 另外,请注意,我从来没有使用过
Marshal.FinalReleaseComObject
——一些COM对象是单例的,它不能很好地处理这些对象


禁止在终结器内的托管对象中执行任何操作(或从众所周知的
IDisposable
实现中进行处置(false))。您不能依赖终结器中的任何.NET对象引用。您可以释放
IntPtr
,但不能释放COM对象,因为它可能已经被清除。

这里有一篇文章:

简言之:

在GC自然发生之前,com引用不会被完全释放。这就是为什么这么多人需要使用FinalReleaseComObject()和GC.Collect()强制销毁对象。脏互操作代码都需要这两种方法

GC不会自动调用Dispose。当释放对象时,会调用析构函数(在不同的线程中)。这通常是您可以释放任何非托管内存或com引用的地方

析构函数:

…当应用程序封装非托管资源(如windows、文件和网络连接)时,应使用析构函数释放这些资源。当对象符合销毁条件时,垃圾收集器将运行对象的Finalize方法

首先-您不必调用
Marshal.ReleaseComObject(…)
Marshal.FinalReleaseComObject(…)
在执行Excel互操作时。这是一种令人困惑的反模式,但任何有关这方面的信息(包括来自Microsoft的信息)都表明您必须从.NET手动释放COM引用是不正确的。事实上,.NET运行时和垃圾收集器正确地跟踪和清理COM引用

其次,如果要确保在进程结束时清除对进程外COM对象的COM引用(以便Excel进程将关闭),则需要确保垃圾收集器运行。通过调用
GC.Collect()
GC.WaitForPendingFinalizers()正确执行此操作
。调用两次是安全的,end可以确保周期也被清理干净

第三,在调试器下运行时,本地引用将被人为地保持活动状态,直到方法结束(以便本地变量检查工作)调用对于从同一方法清理对象(如
rng.Cells
)无效。您应该将执行COM互操作的代码从GC清理拆分为单独的方法

一般模式是:

Sub WrapperThatCleansUp()

    ' NOTE: Don't call Excel objects in here... 
    '       Debugger would keep alive until end, preventing GC cleanup

    ' Call a separate function that talks to Excel
    DoTheWork()

    ' Now Let the GC clean up (twice, to clean up cycles too)
    GC.Collect()    
    GC.WaitForPendingFinalizers()
    GC.Collect()    
    GC.WaitForPendingFinalizers()

End Sub

Sub DoTheWork()
    Dim app As New Microsoft.Office.Interop.Excel.Application
    Dim book As Microsoft.Office.Interop.Excel.Workbook = app.Workbooks.Add()
    Dim worksheet As Microsoft.Office.Interop.Excel.Worksheet = book.Worksheets("Sheet1")
    app.Visible = True
    For i As Integer = 1 To 10
        worksheet.Cells.Range("A" & i).Value = "Hello"
    Next
    book.Save()
    book.Close()
    app.Quit()

    ' NOTE: No calls the Marshal.ReleaseComObject() are ever needed
End Sub
关于这个问题有很多错误的信息和混乱,包括许多关于MSDN和StackOverflow的帖子


最后说服我仔细查看并找出正确建议的是这篇文章,以及在StackOverflow答案的调试器下发现引用保持活动状态的问题。

这提醒我手动内存管理不是一个好主意。MDA发出警告,因为COM服务中存在潜在的硬崩溃r、 尝试在应用程序关闭时处理任何内容都是毫无意义的,它将在毫秒后完成。GC已经知道如何执行此操作,请避免帮助。如果使用COM对象的类将一直存在到应用程序终止,并且如果COM对象的“文档”要求“调用某种
停用
方法”,该怎么办?(我不确定它是做什么的,但我试着不调用它,没有什么不好的事情发生。)为什么我们不应该调用GC.WaitForPendingFinalizers()跟进的GC.Collect()?我自己也不是100%确定,但如果你读了这篇文章,它会说“GC.Collect将停止操作系统中的所有.NET进程以收集垃圾。每次调用GC.Collect时,您可以将垃圾从第0代升级到第1代,然后升级到第2代。垃圾到达第2代后,您必须终止进程以恢复内存。“因此,可能是因为COM对象可能由于强制执行太多GC调用而在GC中停留太久。更频繁的是对GC.Collect的调用会阻止GC及时释放内存。这是因为长寿命对象(即第2代)GC检查对象的频率较低;问题是每次GC跳过对象时都会促进对象的生成(即,对该对象的引用仍然存在),因此,通过调用GC.Collect对象会提前升级,从而诱使GC认为它们是长寿命的,即使它们不是。如果效果与我所说的完全相同,减去可能需要终止应用程序的部分(在许多情况下,我发现这是真的).我当然希望你不要因为不同意我的评论而对答案投反对票。如果你对答案本身有异议,请解释。如果不是你,那么是谁在读这篇文章
Sub WrapperThatCleansUp()

    ' NOTE: Don't call Excel objects in here... 
    '       Debugger would keep alive until end, preventing GC cleanup

    ' Call a separate function that talks to Excel
    DoTheWork()

    ' Now Let the GC clean up (twice, to clean up cycles too)
    GC.Collect()    
    GC.WaitForPendingFinalizers()
    GC.Collect()    
    GC.WaitForPendingFinalizers()

End Sub

Sub DoTheWork()
    Dim app As New Microsoft.Office.Interop.Excel.Application
    Dim book As Microsoft.Office.Interop.Excel.Workbook = app.Workbooks.Add()
    Dim worksheet As Microsoft.Office.Interop.Excel.Worksheet = book.Worksheets("Sheet1")
    app.Visible = True
    For i As Integer = 1 To 10
        worksheet.Cells.Range("A" & i).Value = "Hello"
    Next
    book.Save()
    book.Close()
    app.Quit()

    ' NOTE: No calls the Marshal.ReleaseComObject() are ever needed
End Sub