它是安全地调用从一个终结的RCW? [英] Is it safe to call an RCW from a finalizer?

查看:212
本文介绍了它是安全地调用从一个终结的RCW?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个托管对象调用COM服务器分配一些内存。被管理对象必须再次调用COM服务器来释放内存之前,管理对象消失,以避免内存泄漏。该对象实现的IDisposable ,以确保正确的内存释放COM调用。

在事件的处置方法的没有的叫,我想该对象的终结,以释放内存。麻烦的是,最后确定的规则是,你不能访问任何引用,因为你不知道还有什么其他的对象已经GC'd和/或之前完成。这使得仅可触摸对象状态为字段(手柄是最常见的)。

不过,调用COM服务器包括以自由,我有一个cookie存储在一个领域内存在经历一个运行时可调用包装(RCW)。 是RCW安全地从一个终结调用(它是保证尚未GC'd或完成这一点)?

对于那些你不熟悉的定稿,但终结器线程运行在同时其运行的托管应用程序域的背景下,在这些情况下,触摸引用在理论上是好的,定稿也发生在应用程序域关机,而以任意顺序的 - 不只是在引用关系的顺序。这限制了你可以认为是安全的从你的终结触摸。任何对管理对象可能是坏(收集的内存),即使引用非空。

更新:我刚才试了一下,得到这样的:

  

型System.Runtime.InteropServices.InvalidComObjectException的未处理的异常MyAssembly.dll程序发生

     

更多信息:已被分开与其基础RCW不能使用COM对象

解决方案

我发现从CLR团队本身确实是不安全 - 的除非的你分配的GCHandle的RCW,而它的还是安全的这样做(当您第一次获得RCW)。这确保了GC和终结,需要调用它完成了管理对象前没有累计完成RCW。

 类MyManagedObject:IDisposable的
{
    私人ISomeObject comServer;
    私人的GCHandle rcwHandle;
    私人的IServiceProvider的ServiceProvider;
    私人UINT饼干;

    公共MyManagedObject(的IServiceProvider的ServiceProvider)
    {
        this.serviceProvider =的ServiceProvider;
        this.comServer =这一点。 serviceProvider.GetService(/ *某些服务* /)作为ISomeObject;
        this.rcwHandle = GCHandle.Alloc(this.comServer,GCHandleType.Normal);
        this.cookie = comServer.GetCookie();
    }

    〜MyManagedObject()
    {
        this.Dispose(假);
    }

    公共无效的Dispose()
    {
        this.Dispose(真正的);
        GC.Sup pressFinalize(本);
    }

    受保护的虚拟无效的Dispose(BOOL处置)
    {
        如果(处置)
        {
            //这里处理所有的管理对象。
        }

        如果(this.rcwHandle.IsAllocated)
        {
            //调用这个RCW是安全的,因为我们有一个GC句柄。
            this.comServer.ReleaseCookie(this.cookie);

            //现在松开的GC手柄上的RCW所以它可以被释放以及
            this.rcwHandle.Free();
        }
    }
}
 

原来在我的尤其的情况下,我的应用程序托管CLR本身。因此,它的调用mscoree!终结线程获取CoEEShutdownCOM运行之前,它可以杀死的RCW,并导致 InvalidComObjectException 错误我所看到。

但在正常情况下CLR没有托管本身,有人告诉我这应该工作。

I have a managed object that calls a COM server to allocate some memory. The managed object must call the COM server again to free that memory before the managed object goes away to avoid a memory leak. This object implements IDisposable to help ensure that the correct memory-releasing COM call is made.

In the event that the Dispose method is not called, I would like the object's finalizer to free the memory. The trouble is, the rules of finalization is that you must not access any reference because you don't know what other objects have already been GC'd and/or finalized before you. This leaves the only touchable object state to be fields (handles being the most common).

But calling a COM server involves going through an runtime callable wrapper (RCW) in order to free the memory that I have a cookie to stored in a field. Is that RCW safe to call from a finalizer (is it guaranteed to have not been GC'd or finalized at this point)?

For those of you not familiar with finalization, although the finalizer thread runs in the background of a managed appdomain while its running, at for those cases touching references would theoretically be OK, finalization also happens at appdomain shutdown, and in any order -- not just in reference relationship order. This limits what you can assume is safe to touch from your finalizer. Any reference to a managed object might be "bad" (collected memory) even though the reference is non-null.

Update: I just tried it and got this:

An unhandled exception of type 'System.Runtime.InteropServices.InvalidComObjectException' occurred in myassembly.dll

Additional information: COM object that has been separated from its underlying RCW cannot be used.

解决方案

I found out from the CLR team themselves that indeed it is not safe -- unless you allocate a GCHandle on the RCW while it's still safe to do so (when you first acquire the RCW). This ensures that the GC and finalizer haven't totaled the RCW before the managed object that needs to invoke it is finalized.

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();
        }
    }
}

It turns out in my particular case, my app is hosting the CLR itself. Therefore, it's calling mscoree!CoEEShutdownCOM before the finalizer thread gets to run, which kills the RCW and results in the InvalidComObjectException error I was seeing.

But in normal cases where the CLR is not hosting itself, I'm told this should work.

这篇关于它是安全地调用从一个终结的RCW?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆