2009-10-15 7 views
16

Ich habe ein verwaltetes Objekt, das einen COM-Server zum Reservieren von Speicherplatz aufruft. Das verwaltete Objekt muss den COM-Server erneut aufrufen, um diesen Speicher freizugeben, bevor das verwaltete Objekt entfernt wird, um einen Speicherverlust zu vermeiden. Dieses Objekt implementiert IDisposable, um sicherzustellen, dass der korrekte speicherauslösende COM-Aufruf erfolgt.Ist es sicher, einen RCW von einem Finalizer aus aufzurufen?

Für den Fall, dass die Dispose Methode ist nicht aufgerufen, möchte ich den Finalizer des Objekts, um den Speicher zu befreien. Das Problem ist, dass die Finalisierungsregeln darin bestehen, dass Sie nicht auf eine Referenz zugreifen dürfen, weil Sie nicht wissen, welche anderen Objekte bereits vor Ihnen abgeschlossen und/oder finalisiert wurden. Dies lässt den einzigen berührbaren Objektstatus als Felder (Griffe sind am häufigsten).

Aber das Aufrufen eines COM-Servers beinhaltet das Durchlaufen eines Runtime Callable Wrapper (RCW), um den Speicher freizugeben, in dem ein Cookie in einem Feld gespeichert ist. Kann RCW sicher von einem Finalizer aus angerufen werden (ist es garantiert, dass zu diesem Zeitpunkt noch kein GC oder finalisiert wurde)?

Für diejenigen von Ihnen nicht vertraut mit Abschluss, obwohl die Finalizerthread im Hintergrund eines verwalteten Appdomain läuft während seines Lauf, um für die Fälle, Referenzen zu berühren theoretisch wäre in Ordnung, geschieht Finalisierung auch bei Appdomain Herunterfahren und in beliebiger Reihenfolge - nicht nur in der Reihenfolge der Referenzbeziehung. Dies beschränkt das, was Sie von Ihrem Finalizer als sicher erachten können. Jeder Verweis auf ein verwaltetes Objekt ist möglicherweise "fehlerhaft" (gesammelter Speicher), obwohl der Verweis nicht null ist.

aktualisieren: Ich habe versucht, es einfach und bekam dies:

Eine nicht behandelte Ausnahme des Typs 'System.Runtime.InteropServices.InvalidComObjectException' in MyAssembly.dll

Zusätzliche Informationen aufgetreten: COM-Objekt das von seinem zugrundeliegenden RCW getrennt wurde, kann nicht verwendet werden.

+0

Eine Methode auf dem COM-Server (warum sollte jemand Dispose auf einem RCW selbst nennen? Ich wäre überrascht, wenn das überhaupt möglich wäre). –

Antwort

16

fand ich von der CLR-Team sich heraus, dass in der Tat es nicht sicher ist - es sei denn, Sie GCHandle auf dem RCW zuordnen, während es noch sicher ist, dies zu tun (wenn Sie zuerst die RCW erwerben). Dadurch wird sichergestellt, dass GC und Finalizer den RCW nicht aufsummiert haben, bevor das verwaltete Objekt, das es aufrufen muss, abgeschlossen ist.

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

Es stellt sich heraus in meinem insbesondere Fall, meine App selbst die CLR hostet. Daher ruft es mscoree! CoEEShutdownCOM auf, bevor der Finalizer-Thread ausgeführt wird. Dadurch wird der RCW beendet und der Fehler InvalidComObjectException wird angezeigt.

Aber in normalen Fällen, in denen die CLR nicht selbst hostet, wurde mir gesagt, das sollte funktionieren.

+0

Hallo Andrew, und danke für diesen Beitrag.Ich versuche, Ihre vorgeschlagene Lösung auf eine Klasse anzuwenden, die die IAudioSessionControl2-COM-Schnittstelle umschließt. Class implementiert die 'IDisposable'-Schnittstelle, ruft 'Marshal.ReleaseComObject' über die' IAudioSessionControl2'-Schnittstelleninstanz auf, was zu dem Fehler führt, auf den Sie in Ihrem Beitrag verweisen. Um Ihren Vorschlag zu übernehmen, habe ich "ISomeObject" (was ich nicht kenne) durch "object" ersetzt, weiß aber nicht, wie das 'serviceProvider' -Argument des Konstruktors verwendet werden soll und welcher Dienst beim Aufruf angegeben werden soll zu 'GetService'? Herzlichen Dank. – Bliss

7

Nein, es ist nicht sicher, über den Finalizer-Thread auf einen RCW zuzugreifen. Sobald Sie den Finalizer-Thread erreicht haben, haben Sie keine Garantie mehr, dass der RCW noch am Leben ist. Es ist möglich, dass es Ihrem Objekt in der Finalizer-Warteschlange voraus ist und somit freigegeben wird, wenn Ihr Destruktor im Finalizer-Thread ausgeführt wird.

+0

Entfernte meine Antwort, wie es in meinem Fall erscheint Ich hatte Glück mit dem Finalizer-Thread. – user7116

Verwandte Themen