2016-06-19 35 views
8

Jest to bardzo częste pytanie i postanowiłem je zadać, ponieważ na to pytanie może być inna odpowiedź niż dzisiaj. Mamy nadzieję, że odpowiedzi pomogą zrozumieć, jaki jest właściwy sposób pracy z obiektami COM. Osobiście czuję się bardzo zakłopotany po uzyskaniu różnych opinii na ten temat.Na dzień dzisiejszy, jaki jest właściwy sposób pracy z obiektami COM?

ciągu ostatnich 5 lat, kiedyś pracować z obiektami COM oraz zasady były jasne dla mnie:

  1. Użyj pojedynczej kropki w liniach kodu. Korzystając z więcej niż jednego okresu, twórz tymczasowe obiekty za sceną, których nie można wyraźnie wydać.
  2. Nie używaj foreach, zamiast tego użyj pętli for i zwolnij każdy element w każdej iteracji.
  3. Nie wywołuj FInalReleaseComObject, zamiast tego użyj ReleaseComObject.
  4. Nie należy używać GC do zwalniania obiektów COM. Intencją GC jest głównie debugowanie użycia.
  5. Zwolnij obiekty w kolejności odwrotnej do ich utworzenia.

Niektórzy z was mogą być sfrustrowani po przeczytaniu tych ostatnich linijek, to jest to, co wiedziałem o tym, jak prawidłowo utworzyć/wydać obiekt Com, mam nadzieję uzyskać odpowiedzi, które sprawią, że będzie on bardziej przejrzysty i niekwestionowany.

Poniżej znajdują się linki, które znalazłem na ten temat. Niektóre z nich mówią, że trzeba wywołać ReleaseComObject, a niektóre nie.

”.. W scenariuszach VSTO zwykle nie musisz używać ReleaseCOMObject. ""

"... Należy użyć tej metody, aby uwolnić podstawowy obiekt COM, który przechowuje referencje ..."

UPDATE:

To pytanie zostało oznaczone jako zbyt szeroka. Zgodnie z życzeniem postaram się uprościć i zadać prostsze pytania.

  1. Czy funkcja ReleaseComObject jest wymagana podczas pracy z obiektami COM lub prawidłowym sposobem wywoływania GC?
  2. Czy podejście VSTO zmienia sposób, w jaki pracowaliśmy z obiektami COM?
  3. Które z powyższych reguł, które napisałem, są wymagane i które są nieprawidłowe? Czy są jeszcze jakieś inne?
+0

COM jest COM, i od dzisiaj pozostaje COM. Nie sądzę, aby odpowiedzialność za uwolnienie zasobów od dewelopera. strona i jej użycie oraz administracja w .NET zmieniły się/ewoluowały w jakiś sposób, w każdym razie nie jestem niezarządzanym kodem gurú. Czy istnieje jednak dobry powód, dla którego stosujesz trzecią zasadę przez te wszystkie lata ?. 'FinalReleaseComObject' powinien uwolnić jednocześnie liczbę referencyjną RCW, unikając zbędnych pojedynczych wywołań do' ReleaseCOMObject'. Zobacz tę odpowiedź: http://stackoverflow.com/questions/1827059/why-use-finalreleasecomobject-instead-of-releasecomobject – ElektroStudios

+0

@ElektroStudios, a nawet FinalReleaseComObject opublikuje wszystkie liczniki referencyjne RCW na raz, ale dla lepszego zarządzania, celu debugowania, Wolałem używać ReleaseComObject. Korzystanie z FinalReleaseComObject może szybko doprowadzić do awarii programu z komunikatem "Obiekt COM oddzielony od jego bazowej RCW". – ehh

+0

Dlaczego w ogóle trzeba ręcznie zwolnić obiekty COM, zamiast polegać na GC, aby ostatecznie je wyczyścić? na przykład czy obiekty COM przechowują ograniczone zasoby, takie jak obsługa plików itp. – adrianm

Odpowiedz

10

Interfejs .NET/COM jest dobrze zaprojektowany i działa poprawnie. W szczególności .Barget Garbage Collector poprawnie śledzi odniesienia COM i poprawnie wypuści obiekty COM, gdy nie będą zawierały żadnych odniesień do środowiska wykonawczego. Zakłócenie liczby odwołań obiektu COM przez wywołanie Marshal.ReleaseComObject(...) lub Marshal.FinalReleaseComObject(...) jest niebezpiecznym, ale powszechnym wzorcem zapobiegającym. Niestety, niektóre złe rady wyszły z Microsoftu.

Twój kod .NET może poprawnie współdziałać z COM, ignorując wszystkie 5 reguł. Jeśli trzeba wywołać deterministyczne oczyszczanie obiektów COM, które nie są już przywoływane z środowiska wykonawczego, można bezpiecznie wymusić GC (i ewentualnie czekać na ukończenie finalizatorów). W przeciwnym razie nie musisz robić nic specjalnego w swoim kodzie, aby poradzić sobie z obiektami COM.

Jest jedno ważne zastrzeżenie, które mogło przyczynić się do pomyłki w kwestii roli śmieciarza. Podczas debugowania kodu .NET, zmienne lokalne sztucznie mają wydłużony czas życia do końca metody, w celu wsparcia oglądania zmiennych pod debuggerem. Oznacza to, że możesz nadal zarządzać odniesieniami do obiektu COM (a więc GC nie oczyści się) później niż oczekiwać od samego spojrzenia na kod. Dobrym sposobem obejścia tego problemu (który występuje tylko w debugerze) jest podział zakresu wywołań COM z wywołań czyszczenia GC.

Oto przykład kodu C#, który współdziała z programem Excel i czyści poprawnie. Możesz wkleić do aplikacji konsoli (wystarczy dodać odwołanie do Microsoft.Office.Interop.Excel):

using System; 
using System.Runtime.InteropServices; 
using Microsoft.Office.Interop.Excel; 

namespace TestCsCom 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      // NOTE: Don't call Excel objects in here... 
      //  Debugger would keep alive until end, preventing GC cleanup 

      // Call a separate function that talks to Excel 
      DoTheWork(); 

      // Now let the GC clean up (repeat, until no more) 
      do 
      { 
       GC.Collect(); 
       GC.WaitForPendingFinalizers(); 
      } 
      while (Marshal.AreComObjectsAvailableForCleanup()); 
     } 

     static void DoTheWork() 
     { 
      Application app = new Application(); 
      Workbook book = app.Workbooks.Add(); 
      Worksheet worksheet = book.Worksheets["Sheet1"]; 
      app.Visible = true; 
      for (int i = 1; i <= 10; i++) { 
       worksheet.Cells.Range["A" + i].Value = "Hello"; 
      } 
      book.Save(); 
      book.Close(); 
      app.Quit(); 

      // NOTE: No calls the Marshal.ReleaseComObject() are ever needed 
     } 
    } 
} 

zobaczysz, że proces Excel poprawnie wyłącza się, wskazując, że wszystkie obiekty COM zostały odpowiednio oczyszczone w górę.

VSTO nie zmienia żadnego z tych problemów - jest to po prostu biblioteka .NET, która owija i rozszerza natywny model obiektu Office COM.


Istnieje wiele fałszywych informacji i nieporozumień związanych z tym problemem, w tym wiele postów na MSDN i StackOverflow.

To, co w końcu przekonało mnie do bliższego przyjrzenia się i znalezienia właściwej porady, było tym razem https://blogs.msdn.microsoft.com/visualstudio/2010/03/01/marshal-releasecomobject-considered-dangerous/ wraz ze znalezieniem problemu z referencjami utrzymywanymi przy życiu w ramach debuggera w odpowiedzi na StackOverflow.


Jednym wyjątkiem od tego ogólnego poradnika jest sytuacja, gdy model obiektów COM wymaga zwolnienia interfejsów w określonej kolejności. Opisane tutaj podejście GC nie daje kontroli nad kolejnością, w której obiekty COM są publikowane przez GC.

Nie mam żadnego odniesienia, aby wskazać, czy naruszałoby to umowę z Komisją. Ogólnie, oczekiwałbym, że hierarchie COM będą używać wewnętrznych odniesień, aby zapewnić właściwe zarządzanie zależnościami w sekwencji. Na przykład. w przypadku Excela można oczekiwać, że obiekt Range będzie zachowywał wewnętrzne odwołanie do nadrzędnego obiektu Worksheet, dzięki czemu użytkownik modelu obiektowego nie musi jawnie utrzymywać obydwu przy życiu.

Może się zdarzyć, że nawet aplikacje Office będą czułe na sekwencję zwalniania obiektów COM.Wydaje się, że jest jeden przypadek, gdy używane jest osadzanie OLE - patrz: https://blogs.msdn.microsoft.com/vsofficedeveloper/2008/04/11/excel-ole-embedding-errors-if-you-have-managed-add-in-sinking-application-events-in-excel-2/

Możliwe byłoby stworzenie modelu obiektu COM, który nie powiedzie się, jeśli obiekty zostaną zwolnione w niewłaściwej kolejności, a współdziałanie z takim modelem COM byłoby wówczas możliwe. wymagają większej ostrożności i mogą wymagać ręcznej ingerencji w referencje.

Ale dla ogólnego współdziałania z modelami obiektów Office COM, zgadzam się z VS blog post nazywając "Marshal.ReleaseComObject - problem ukryty jako rozwiązanie".

+0

10 połączeń, aby udowodnić tę koncepcję, poważnie? Chodź, przynajmniej spróbuj z 10M :) –

+0

@IvanStoev Moja cierpliwość na ten temat była już dość cienka. – Govert

+2

Dodałbym, że istnieje wiele obiektów COM tam zapisanych w niezarządzanym kodzie, które mają błędy (nie mówiąc o dużych, które są ogólnie ok). Błędy te przyczyniają się do fałszywego wrażenia, że ​​P/Invoke jest zepsuty, ponieważ czasami zmuszają programistę .NET do zastosowania panicznej magii voodoo COM (dodaj ReleaseComObject wszędzie, zwolnij obiekty w odwrotnej kolejności itp.) –