2009-10-15 8 views
16

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 el objeto administrado desaparezca para evitar una pérdida de memoria. Este objeto implementa IDisposable para ayudar a garantizar que se realice la llamada COM de liberación de memoria correcta.¿Es seguro llamar a un RCW desde un finalizador?

En el caso de que el método Dispose sea no llamado, me gustaría que el finalizador del objeto libere la memoria. El problema es que las reglas de finalización son que no debe acceder a ninguna referencia porque no sabe qué otros objetos ya han sido procesados ​​y/o finalizados antes que usted. Esto deja el único estado de objeto touchable como campos (los identificadores son los más comunes).

Pero llamar a un servidor COM implica pasar por un contenedor invocable en tiempo de ejecución (RCW) para liberar la memoria en la que tengo una cookie almacenada en un campo. ¿Es seguro llamar a RCW desde un finalizador (¿está garantizado que no se ha completado o no el GC en este momento)?

Para aquellos que no estén familiarizados con la finalización, aunque el subproceso finalizador ejecuta en segundo plano de un dominio de aplicación gestionada mientras que su ejecución, en esos casos que tocan referencias teóricamente estar bien, finalización también sucede en el apagado dominio de aplicación, y en cualquier orden - no solo 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 gestionado podría ser "mala" (memoria recopilada) aunque la referencia no sea nula.

actualización: Sólo lo intentó y consiguió esto:

Una excepción no controlada del tipo 'System.Runtime.InteropServices.InvalidComObjectException' producido en MyAssembly.dll

Información adicional: objeto COM que se ha separado de su RCW subyacente no se puede usar.

+0

Un método en el servidor COM (¿por qué alguien llamaría a Dispose en un RCW? Me sorprendería si eso fuera posible). –

Respuesta

16

Me enteré por el equipo CLR que no es seguro - a menos que asigne un GCHandle en el RCW mientras aún es 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 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 particular, caso, mi aplicación es el anfitrión del CLR. Por lo tanto, está llamando a mscoree! CoEEShutdownCOM antes de que se ejecute el hilo del finalizador, lo que mata al RCW y da como resultado el error InvalidComObjectException que estaba viendo.

Pero en los casos normales en que el CLR no se aloja, me dicen que esto debería funcionar.

+0

Hola Andrew, y gracias por esta publicación.Estoy tratando de aplicar la solución sugerida a una clase que incluya la interfaz COM 'IAudioSessionControl2'. Class implementa la interfaz 'IDisposable', llama' Marshal.ReleaseComObject' sobre la instancia de interfaz 'IAudioSessionControl2', lo que da como resultado el error al que se refiere en su publicación. Viniendo a aplicar tu sugerencia, reemplacé 'ISomeObject' (que no estoy familiarizado) con' object', pero no sé cómo usar el argumento 'serviceProvider' del constructor, y qué servicio debe indicarse en la llamada a 'GetService'? Muy agradecido. – Bliss

7

No, no es seguro acceder a un RCW desde el hilo del finalizador. Una vez que alcanzas el hilo del finalizador no tienes garantía de que el RCW todavía esté vivo. Es posible que esté delante de su objeto en la cola del finalizador y, por lo tanto, se libere cuando su destructor se ejecute en la secuencia del finalizador.

+0

Se eliminó mi respuesta, ya que parece que en mi caso tuve suerte con el hilo del finalizador. – user7116

Cuestiones relacionadas