.net 从终结器调用RCW安全吗?

.net 从终结器调用RCW安全吗?,.net,com,finalizer,.net,Com,Finalizer,我有一个托管对象,它调用COM服务器来分配一些内存。在托管对象离开之前,托管对象必须再次调用COM服务器以释放该内存,以避免内存泄漏。此对象实现了IDisposable,以帮助确保进行了正确的内存释放COM调用 如果未调用Dispose方法,我希望对象的终结器释放内存。问题是,终结的规则是,您不能访问任何引用,因为您不知道在您之前已经GC和/或终结了哪些其他对象。这使得唯一可触摸的对象状态为字段(句柄是最常见的) 但是调用COM服务器需要通过运行时可调用包装器(RCW)来释放存储在字段中的coo

我有一个托管对象,它调用COM服务器来分配一些内存。在托管对象离开之前,托管对象必须再次调用COM服务器以释放该内存,以避免内存泄漏。此对象实现了
IDisposable
,以帮助确保进行了正确的内存释放COM调用

如果未调用
Dispose
方法,我希望对象的终结器释放内存。问题是,终结的规则是,您不能访问任何引用,因为您不知道在您之前已经GC和/或终结了哪些其他对象。这使得唯一可触摸的对象状态为字段(句柄是最常见的)

但是调用COM服务器需要通过运行时可调用包装器(RCW)来释放存储在字段中的cookie内存从终结器调用RCW安全吗(是否保证此时未被GC’d或终结)?

对于那些不熟悉终结的人来说,虽然终结器线程在运行时在托管appdomain的后台运行,但对于那些涉及引用的情况,理论上是可以的,终结也会在appdomain关闭时发生,并且以任何顺序发生,而不仅仅是以引用关系顺序。这限制了您可以假设从终结器安全地触摸的内容。对托管对象的任何引用都可能是“坏”(收集的内存),即使该引用为非空

更新:我刚刚尝试了一下,得到了以下结果:

myassembly.dll中发生类型为“System.Runtime.InteropServices.InvalidComObjectException”的未处理异常

其他信息:无法使用已与其基础RCW分离的COM对象


不,从终结器线程访问RCW是不安全的。一旦到达终结器线程,就不能保证RCW仍然有效。它可能位于终结器队列中对象的前面,因此在析构函数在终结器线程上运行时释放

我从CLR团队自己那里发现,这确实是不安全的——除非您在RCW上分配GCHandle,而这样做仍然是安全的(当您首次获得RCW时)。这确保了GC和终结器在需要调用RCW的托管对象被终结之前不会对RCW进行合计

class MyManagedObject : IDisposable
{
    private ISomeObject comServer;
    private GCHandle rcwHandle;
    private IServiceProvider serviceProvider;
    private uint cookie;

    public MyManagedObject(IServiceProvider serviceProvider)
    {
        this.serviceProvider = serviceProvider;
        this.comServer = this. serviceProvider.GetService(/*some service*/) as ISomeObject;
        this.rcwHandle = GCHandle.Alloc(this.comServer, GCHandleType.Normal);
        this.cookie = comServer.GetCookie();
    }

    ~MyManagedObject()
    {
        this.Dispose(false);
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // dispose owned managed objects here.
        }

        if (this.rcwHandle.IsAllocated)
        {
            // calling this RCW is safe because we have a GC handle to it.
            this.comServer.ReleaseCookie(this.cookie);

            // Now release the GC handle on the RCW so it can be freed as well
            this.rcwHandle.Free();
        }
    }
}
事实证明,在我的特殊情况下,我的应用程序本身承载着CLR。因此,它叫mscoree!CoEEShutdownCOM在终结器线程运行之前,这会终止RCW并导致我看到的
InvalidComObjectException
错误


但是在CLR没有自己托管的正常情况下,我被告知这应该是可行的。

删除了我的答案,因为在我的情况下,我得到了幸运的终结器线程。COM服务器上的一个方法(为什么有人会调用RCW本身的Dispose?如果可能的话,我会感到惊讶)。你好,安德鲁,谢谢你的这篇文章。我正在尝试将您建议的解决方案应用于包装
IAudioSessionControl2
COM接口的类。类实现
IDisposable
接口,在
IAudioSessionControl2
接口实例上调用
Marshal.ReleaseComObject
,这会导致您在文章中引用的错误。为了应用您的建议,我将
isomoObject
(我不熟悉)替换为
object
,但不知道如何使用构造函数的
serviceProvider
参数,以及调用
GetService
时应该说明什么服务?非常感谢。