2009-10-15 19 views
16

Mam obiekt zarządzany, który wywołuje serwer COM w celu przydzielenia pamięci. Obiekt zarządzany musi ponownie wywołać serwer COM, aby zwolnić tę pamięć, zanim obiekt zarządzany zniknie, aby uniknąć wycieku pamięci. Ten obiekt implementuje IDisposable, aby zapewnić prawidłowe wywołanie COM zwalniające pamięć.Czy można bezpiecznie wywołać RCW z finalizatora?

Jeśli metoda ma być , a nie, chciałbym, aby finalizator obiektu zwolnił pamięć. Kłopot w tym, że zasady finalizacji polegają na tym, że nie możesz uzyskać dostępu do żadnych referencji, ponieważ nie wiesz, jakie inne obiekty zostały już GC'd i/lub sfinalizowane przed tobą. Pozostawia to jedyny dotykany obiekt jako pola (najczęściej są to uchwyty).

Jednak wywoływanie serwera COM wymaga przechodzenia przez opakowanie runtime (RCW) w celu zwolnienia pamięci, której plik cookie jest przechowywany w polu. Czy RCW można bezpiecznie wywołać z finalizatora (czy w tym momencie gwarantowane jest, że nie został GCdd sfinalizowany)?

Dla tych z Was nie zna finalizacji, chociaż wątek finalizator działa w tle zarządzanym AppDomain, a jego bieg, w tych przypadkach dotykających referencje teoretycznie być OK, finalizacja dzieje się również w AppDomain zamykania i w dowolnej kolejności - nie tylko w porządku relacji odniesienia. Ogranicza to, co możesz założyć, że możesz bezpiecznie dotknąć swojego finalizatora. Wszelkie odniesienia do zarządzanego obiektu mogą być "złe" (pamięć zebrana), nawet jeśli odwołanie ma wartość inną niż null.

Aktualizacja: Właśnie spróbowałem i dostałem to:

nieobsługiwany wyjątek typu 'System.Runtime.InteropServices.InvalidComObjectException' wystąpił w myassembly.dll

Informacje dodatkowe: obiekt COM który został oddzielony od podstawowego RCW, nie może być użyty.

+0

Metoda na serwerze COM (dlaczego ktoś miałby nazywać się Dispose na samym RCW? Byłbym zaskoczony, gdyby to było możliwe). –

Odpowiedz

16

Dowiedziałem się od samego zespołu CLR, że rzeczywiście nie jest to bezpieczne - , chyba że przeznaczysz GCHandle na RCW, podczas gdy nadal jest to bezpieczne (kiedy po raz pierwszy nabywasz RCW). Gwarantuje to, że GC i finalizator nie sumują RCW zanim sfinalizowany obiekt zarządzany, który wymaga wywołania, zostanie zakończony.

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

Okazuje się moim szczególności przypadku, moja aplikacja jest gospodarzem samego CLR. Dlatego wywołuje funkcję mscoree! CoEEShutdownCOM, zanim wątek finalizera zostanie uruchomiony, co zabija RCW i powoduje błąd, który widziałem.

Ale w normalnych przypadkach, w których CLR nie jest hostem, powiedziano mi, że to powinno działać.

+0

Cześć Andrew, i dzięki za ten post.Próbuję zastosować proponowane rozwiązanie do klasy owijającej interfejs COM 'IAudioSessionControl2'. Klasa implementuje interfejs 'IDisposable', wywołuje' Marshal.ReleaseComObject' przez instancję interfejsu 'IAudioSessionControl2', co powoduje błąd, do którego odwołujesz się w swoim wpisie. Wracając do zastosowania twojej sugestii, zastąpiłem 'ISomeObject' (który nie jest mi znany)' object', ale nie wiem jak używać argumentu 'serviceProvider' konstruktora, i jaką usługę należy podać w wywołaniu do "GetService"? Bardzo zobowiązany. – Bliss

7

Nie jest bezpieczny dostęp do RCW z wątku finalizatora. Po dotarciu do wątku finalizatora nie masz żadnej gwarancji, że RCW wciąż żyje. Możliwe, że znajduje się on przed twoim obiektem w kolejce finalizatora, a tym samym jest zwolniony, gdy twój destruktor uruchomi wątek finalizatora.

+0

Usunięto moją odpowiedź, ponieważ pojawia się w moim przypadku Mam szczęście w wątku finalizatora. – user7116

Powiązane problemy