Безопасное освобождение ссылки на COM-объект из .NET

StackOverflow https://stackoverflow.com/questions/1834950

  •  11-09-2019
  •  | 
  •  

Вопрос

Я прочитал много статей в сети о безопасном выпуске RCW, и мне кажется, что никто не может прийти к единому мнению, что именно и в каком порядке нужно делать, поэтому я прошу вас, ребята, высказать свое мнение.Например, можно было бы сделать это:

object target = null;
try {
    // Instantiate and use the target object.
    // Assume we know what we are doing: the contents of this try block
    // do in fact represent the entire desired lifetime of the COM object,
    // and we are releasing all RCWs in reverse order of acquisition.
} finally {
    if(target != null) {
        Marshal.FinalReleaseComObject(target);
        target = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}

Тем не менее, некоторые люди рекомендуют выполнять сбор мусора перед Marshal.FinalReleaseComObject, кто-то после, а кто-то вообще нет.Действительно ли необходимо вручную собирать каждый RCW, особенно после того, как он уже отсоединен от своего COM-объекта?

На мой взгляд, было бы проще и проще просто отсоединить RCW от COM-объекта и оставить срок действия RCW естественным образом:

object target = null;
try {
    // Same content as above.
} finally {
    if(target != null) {
        Marshal.FinalReleaseComObject(target);
    }
}

Достаточно ли этого сделать?

Это было полезно?

Решение

Чтобы освободить ссылку на целевой COM-объект, достаточно и предпочтительный просто позвонить Marshal.FinalReleaseComObject и нет заставить собирать.Другими словами, вы выполнили свою обязанность опубликовать ссылку, как только закончите с ней.Я не буду касаться вопроса FinalReleaseComObject против ReleaseComObject.

Это оставляет более серьезный вопрос: почему люди выступают за звонки GC.Collect() и WaitForPendingFinalizers()?

Поскольку в некоторых проектах трудно определить, что управляемых ссылок больше нет, и вы не можете безопасно вызвать ReleaseComObject.У вас есть два варианта: позволить памяти накопиться и надеяться, что сбор произойдет, или принудительно собрать сбор.[см. комментарий Стивена Янсена о голосовании против]

Дополнительное примечание: настройка target к null обычно не требуется, и особенно в вашем примере кода.Обнуление объектов является обычной практикой для VB6, поскольку он использует сборщик мусора на основе счетчика ссылок.Компилятор C# достаточно умен (при сборке для выпуска), чтобы знать, что target недоступен после его последнего использования и может быть GC'd, даже до выхода из области действия.Под последним использованием я подразумеваю последнее возможное использование, поэтому в некоторых случаях вы можете установить его на null.Вы можете убедиться в этом сами с помощью кода ниже:

   using System;
   class GCTest
   {
       ~GCTest() { Console.WriteLine("Finalized"); } 
       static void Main()
       {
           Console.WriteLine("hello");
           GCTest x = new GCTest();
           GC.Collect();
           GC.WaitForPendingFinalizers();
           Console.WriteLine("bye");
       }
   }

Если вы создаете выпуск (например, CSC GCTest.cs), «Finalized» будет печататься между «привет» и «пока».Если вы создаете отладочную версию (например, CSC /debug GCTest.cs), «Finalized» будет распечатано после «bye», тогда как установка x до нуля до Collect() бы это "исправило".

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top