.net - implementing - ¿Es seguro llamar a un RCW desde un finalizador?
implementar dispose c# (2)
Me enteré por el equipo de CLR que, de hecho, no es seguro, a menos que asigne un GCHandle en el RCW mientras aún sea seguro hacerlo (cuando adquiere el RCW por primera vez). Esto garantiza que el GC y el finalizador no hayan totalizado el RCW antes de que se finalice el objeto gestionado que necesita invocarlo.
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();
}
}
}
Resulta que en mi caso particular , mi aplicación aloja el CLR en sí. Por lo tanto, está llamando a mscoree! CoEEShutdownCOM antes de que se ejecute el subproceso finalizador, lo que destruye el RCW y genera el error InvalidComObjectException
que estaba viendo.
Pero en los casos normales en los que el CLR no se aloja solo, me dicen que esto debería funcionar.
Tengo un objeto administrado que llama a un servidor COM para asignar algo de memoria. El objeto administrado debe volver a llamar al servidor COM para liberar esa memoria antes de que desaparezca el objeto administrado para evitar una pérdida de memoria. Este objeto se implementa como IDisposable
para ayudar a garantizar que se realice la llamada COM correcta de liberación de memoria.
En el caso de que no se llame al método Dispose
, me gustaría que el finalizador del objeto libere la memoria. El problema es que las reglas de finalización es que no debe acceder a ninguna referencia porque no sabe qué otros objetos ya han sido GC y / o finalizados antes que usted. Esto deja el único estado de objeto que se puede tocar para que sea campos (los controles son los más comunes).
Pero llamar a un servidor COM implica pasar por un envoltorio que se puede cerrar en tiempo de ejecución (RCW) para liberar la memoria que tengo para almacenar una cookie en un campo. ¿Es seguro llamar a RCW desde un finalizador (se garantiza que no se ha completado ni finalizado por GC en este momento)?
Para aquellos de ustedes que no están familiarizados con la finalización, aunque el subproceso del finalizador se ejecuta en el fondo de un dominio de aplicación administrado mientras se está ejecutando, en los casos en que las referencias a los toques serían teóricamente correctas, la finalización también ocurre en el cierre de la aplicación y no en cualquier orden . Sólo en el orden de relación de referencia. Esto limita lo que puede asumir que es seguro tocar desde su finalizador. Cualquier referencia a un objeto administrado puede ser "mala" (memoria recopilada) aunque la referencia no sea nula.
Actualización : Acabo de probarlo y obtuve esto:
Se produjo una excepción no controlada de tipo ''System.Runtime.InteropServices.InvalidComObjectException'' en myassembly.dll
Información adicional: no se puede utilizar el objeto COM que se ha separado de su RCW subyacente.
No, no es seguro acceder a un RCW desde el subproceso finalizador. Una vez que llegue al subproceso finalizador, no tiene garantía de que el RCW aún esté vivo. Es posible que esté delante de su objeto en la cola del finalizador y, por lo tanto, se libere para cuando el destructor se ejecute en el subproceso del finalizador.