我有一个托管对象,该对象调用COM服务器分配一些内存。被管理对象必须消失之前,必须再次调用COM服务器以释放该内存,以免发生内存泄漏。此对象实现IDisposable
以帮助确保进行正确的释放内存的COM调用。
如果没有调用Dispose
方法,我希望对象的终结器释放内存。问题在于,终结处理的规则是您不得访问任何引用,因为您不知道在您进行GC和/或终结处理之前还有哪些其他对象。这使唯一可触摸的对象状态为字段(句柄是最常见的)。
但是,调用COM服务器涉及通过运行时可调用包装(RCW),以释放内存,而我将cookie存储在字段中。从终结器调用该RCW是否安全(是否保证此时尚未进行GC或终结)?
对于那些不熟悉终结处理的人,尽管终结器线程在运行时在托管应用程序域的后台运行,在这种情况下,从理论上讲,可以通过引用进行操作,终结处理也可以在应用程序域关闭时发生,并且以任何顺序进行-只是按引用关系顺序。这限制了您可以认为可以从终结器安全触摸的内容。即使对托管对象的任何引用都是非null的,它也可能是“坏的”(收集的内存)。
更新:我只是试过了,得到了这个:
myassembly.dll中发生了类型为'System.Runtime.InteropServices.InvalidComObjectException'的未处理异常
附加信息:不能使用已与其基础RCW分离的COM对象。
最佳答案
我从CLR团队本身了解到确实不安全-除非您在RCW上分配一个GCHandle时仍然安全(当您首次获得RCW时)。这样可以确保在需要调用它的托管对象完成之前,GC和终结器未对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不托管自己,我被告知这应该可行。
关于.net - 从终结器调用RCW是否安全?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/1573977/