2009-10-15 9 views
16

Possiedo un oggetto gestito che chiama un server COM per allocare memoria. L'oggetto gestito deve chiamare nuovamente il server COM per liberare quella memoria prima che l'oggetto gestito si allontani per evitare una perdita di memoria. Questo oggetto implementa IDisposable per garantire che venga effettuata la chiamata COM di rilascio memoria corretta.È sicuro chiamare un RCW da un finalizzatore?

Nel caso in cui il metodo Dispose sia non chiamato, vorrei che il finalizzatore dell'oggetto liberasse la memoria. Il guaio è che le regole di finalizzazione sono che non devi accedere a nessun riferimento perché non sai quali altri oggetti sono già stati sottoposti a GC e/o finalizzati prima di te. Questo lascia l'unico stato dell'oggetto tangibile come campi (le maniglie sono le più comuni).

Ma chiamare un server COM implica passare attraverso un runtime callable wrapper (RCW) per liberare la memoria che ho un cookie da memorizzare in un campo. E 'sicuro che RCW chiama da un finalizzatore (è garantito che non sia stato eseguito da GC o finalizzato a questo punto)?

Per quelli di voi che non hanno familiarità con la messa a punto, anche se il filo finalizzatore viene eseguito in background di un AppDomain gestito mentre la sua esecuzione, a per i casi che toccano i riferimenti sarebbero teoricamente OK, finalizzazione avviene anche allo spegnimento dominio di applicazione, e in qualsiasi ordine - non solo nell'ordine delle relazioni di riferimento. Questo limita ciò che si può ritenere sia sicuro da toccare dal finalizzatore. Qualsiasi riferimento a un oggetto gestito potrebbe essere "cattivo" (memoria raccolta) anche se il riferimento non è nullo.

Aggiornamento: Ho appena provato e ottenuto questo:

un'eccezione non gestita di tipo 'System.Runtime.InteropServices.InvalidComObjectException' si è verificato in MyAssembly.dll

Ulteriori informazioni: oggetto COM che è stato separato dal suo RCW sottostante non può essere utilizzato.

+0

Un metodo sul server COM (perché qualcuno dovrebbe chiamare Dispose su una RCW stessa? Sarei sorpreso se questo è anche possibile). –

risposta

16

ho scoperto dal team CLR stessi che in effetti non è sicuro - a meno che non allocare un GCHandle sul RCW mentre è ancora sicuro di farlo (prima volta che si acquista il RCW). Ciò garantisce che GC e finalizzatore non abbiano totalizzato RCW prima che l'oggetto gestito che deve richiamarlo sia finalizzato.

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

Si scopre nella mia particolare caso, la mia app ospita il CLR stesso. Pertanto, chiama mscoree! CoEEShutdownCOM prima che venga eseguito il thread del finalizzatore, che uccide RCW e genera l'errore InvalidComObjectException che stavo vedendo.

Tuttavia, nei casi normali in cui il CLR non è in hosting, mi viene detto che dovrebbe funzionare.

+0

Ciao Andrew, e grazie per questo post.Sto cercando di applicare la soluzione suggerita a una classe che avvolge l'interfaccia COM di 'IAudioSessionControl2'. La classe implementa l'interfaccia 'IDisposable', chiama' Marshal.ReleaseComObject' sull'istanza di interfaccia 'IAudioSessionControl2', che genera l'errore a cui ti stai riferendo nel tuo post. Venendo ad applicare il tuo suggerimento, ho sostituito 'ISomeObject' (che non ho familiarità con) con' object', ma non so come usare l'argomento 'serviceProvider' del costruttore e quale servizio dovrebbe essere indicato nella chiamata a 'GetService'? Molto obbligato. – Bliss

7

No, non è sicuro accedere a un RCW dal thread del finalizzatore. Una volta raggiunto il thread del finalizzatore, non si ha alcuna garanzia che RCW sia ancora vivo. È possibile che sia davanti al tuo oggetto nella coda del finalizzatore e quindi rilasciato dal momento in cui il distruttore viene eseguito sul thread del finalizzatore.

+0

Rimossa la mia risposta come appare nel mio caso, ho avuto fortuna con il thread del finalizzatore. – user7116

Problemi correlati