代码之家  ›  专栏  ›  技术社区  ›  Andrew Arnott

从终结器调用RCW是否安全?

  •  16
  • Andrew Arnott  · 技术社区  · 16 年前

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

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

    但是调用一个COM服务器需要经过一个运行时可调用包装器(RCW),以便释放一个存储在字段中的cookie内存。 RCW是否可以安全地从终结器调用(它是否保证在此时没有GC或终结)?

    对于不熟悉终结的人,虽然终结器线程在托管AppDomain运行时在其后台运行,但对于这些情况,接触引用理论上是可以的,终结也在AppDomain关闭时发生,并且 以任何顺序 --不仅仅是参考关系顺序。这限制了您可以假设从终结器可以安全接触到的内容。对托管对象的任何引用都可能是“坏的”(收集的内存),即使引用不是空的。

    更新 :我刚试过,得到了:

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

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

    2 回复  |  直到 13 年前
        1
  •  16
  •   Cédric Guillemette    13 年前

    我从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没有托管自己时,我被告知这应该有效。

        2
  •  7
  •   JaredPar    16 年前

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

    推荐文章