Совместимость .NET и COM: освободить COM от клиента .NET - PullRequest
7 голосов
/ 14 марта 2012

Предположим, у меня есть COM-объект (неуправляемый) и клиент .NET.

Нужно ли вызывать из клиента .NET метод Marshal.FinalReleaseComObject для освобождения COM-объекта?

Ответы [ 2 ]

2 голосов
/ 15 марта 2012

A изменено"Да" и "Нет".

Прежде всего, имейте в виду, что ReleaseComObject не автоматически уменьшает реальный счетчик объектов COM. Скорее это уменьшает внутренний счетчик RCW. (Каждый раз, когда тот же COM-объект перемещается из COM-> NET, он будет использовать тот же RCW и увеличивать счетчик RCW на единицу.) Аналогичным образом, FinalRelaseComObject также влияет на RCW эффективно противостоит "устанавливает его в 0". Когда счетчик RCW обнулится, .NET уменьшит фактический COM ref-count.

Итак, «да», но изменено для этих правил :

  1. Каждый раз, когда объект пересекается с COM-> NET, он должен иметь ReleaseComObject, , но не FinalReleaseComObject, вызванный на него. То есть он должен вызываться один раз каждый раз, когда объект пересекается, , даже если это приводит к RCW .
  2. Количество ссылок на RCW (который является просто прокси-оберткой) не имеет значения; только когда объект пересек границу. Смотрите правило № 1. (Важно контролировать ссылки и кто их хранит, но «худшее», что происходит в этом случае, является исключением из использования «отдельного RCW», которое ничем не отличается от , чем использование потока Disposed.)

Я говорю приведенные выше правила, а не FinalReleaseComObject, поскольку объекты RCW кэшируются в прокси-сервере идентификации, поэтому с использованием FinalReleaseComObject повлияет на COM-> NET пересечения границ даже не знал о ! (Это плохо, и на данный момент я полностью согласен с ответом JaredPars.)

И «нет» в этом, когда RCW будет освобожден (вызывается финализатор), он автоматически «освободит» ссылку COM (фактически вызов FinalReleaseComObject включен сам). Помните, что, поскольку существует только один объект RCW, это означает, что он никогда не будет возвращен, пока есть ссылка на него в .NET. Если RCW исправлен, то нет проблем , потому что сверху нет больше ссылок на упомянутое RCW, и поэтому .NET знает, что он может уменьшить счетчик ссылок COM на единицу (что может вызвать COM-объект) быть уничтоженным).

Однако, поскольку .NET GC является привередливым и недетерминированным , использование этого поведения означает, что время жизни объекта не контролируется. Это может вызвать, например, множество тонких проблем при работе с объектной моделью Outlook. (Он может быть менее подвержен проблемам с другими COM-серверами, но OOM выполняет забавное внутреннее «кеширование» объектов. Легко получить исключение «Элемент уже изменен», если явно не контролировать время жизни.)

В моем коде у меня есть класс ComWrapper, который поддерживает IDisposable. Это позволяет мне передавать объекты RCW, полученные из COM-> NET с чистым сроком владения . После перехода на этот подход у меня было очень мало проблем (на самом деле их практически не было) после перехода на этот подход и многочисленных проблем до этого. Недостатком является то, что необходимо определить границу COM-> NET, но если это так, то внутренне все времена жизни правильно обрабатываются.

Удачного кодирования.

2 голосов
/ 14 марта 2012

Нет. Нет необходимости явно освобождать COM-объект от клиента .Net. COM-объект будет собран, как и любой другой объект .Net, и освободит свой основной дескриптор, как только все ссылки на него будут удалены.

Явное использование FinalReleaseComObject может фактически привести к ошибкам программирования. Если другой компонент в вашем коде все еще ссылается на COM-объект, вы будете вытаскивать нативный объект из-под него. Это может привести к сбоям во время выполнения в будущем.

...