RCW и справочный подсчет при использовании Com Interop в C#

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

Вопрос

У меня есть приложение, которое использует офисные контакты. Мне известно о том, что «Callible Callable Orbper (RCW)» управляется «Продолжаемая среда». Но я не очень уверен, как количество ссылок увеличивается. MSDN говорит,

RCW сохраняет только одну ссылку на обернутый объект COM независимо от количества управляемых клиентов, которые называют его.

Если я понимаю это правильно, в следующем примере,

using Microsoft.Office.Interop.Word;

static void Foo(Application wrd)
{
    /* .... */
}

static void Main(string[] args)
{
    var wrd = new Application();
    Foo(wrd);
    /* .... */
}

Я передаю экземпляр wrd к другому методу. Но это не увеличивает внутреннее количество ссылок. Так что мне интересно, какие сценарии увеличивается количество ссылок? Может ли кто -нибудь указать на сценарий, когда количество ссылок увеличивается?

Также я прочитал какой -то блог, в котором говорится, что избегает использования двойных точек при программировании с объектами COM. Что-то типа, wrd.ActiveDocument.ActiveWindow. Анкет Автор утверждает, что компилятор создает отдельные переменные для сохранения значений, которые будут увеличивать контрольный счетчик. ИМХО, это неправильно, и первый пример доказывает это. Это правильно?

Любая помощь была бы отличной!

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

Решение

Я также изучал этот вопрос, работая над приложением, ориентированным на Com/.net-Interop, борьбы с утечками, висячими и авариями.

Короткий ответ: каждый раз, когда объект COM передается из среды COM в .NET.

Длинный ответ:

  1. Для каждого объекта COM есть один объект RCW [Тест 1] [Ссылка 4
  2. Количество ссылок увеличивается каждый раз, когда объект запрашивается из объекта COM (свойство вызова или метод на объекте COM, который возвращает объект COM, возвращаемое количество ссылок на объект COM будет увеличено на один) [Тест 1
  3. Ссылочное количество не увеличивается путем подчинения к другим интерфейсам COM объекта или перемещением ссылки RCW вокруг [Тест 2
  4. Ссылочное количество увеличивается каждый раз, когда объект передается в качестве параметра в событии, поднятое com [Ref 1

С другой стороны: вы должны ВСЕГДА Выпустите Com объекты, как только вы закончите их использовать. Оставить эту работу в GC может привести к утечкам, неожиданному поведению и тупикам событий. Это в десять раз важнее, если вы получаете доступ к объекту, а не в потоке STA, на котором он был создан. [Ссылка 2] [Ссылка 3] [болезненный личный опыт

Я надеюсь, что я рассмотрел все чехлы, но COM - жесткое печенье. Ваше здоровье.

Тест 1 - Ссылка

private void Test1( _Application outlookApp )
{
    var explorer1 = outlookApp.ActiveExplorer();
    var count1 = Marshal.ReleaseComObject(explorer1);
    MessageBox.Show("Count 1:" + count1);

    var explorer2 = outlookApp.ActiveExplorer();
    var explorer3 = outlookApp.ActiveExplorer();
    var explorer4 = outlookApp.ActiveExplorer();

    var equals = explorer2 == explorer3 && ReferenceEquals(explorer2, explorer4);
    var count2 = Marshal.ReleaseComObject(explorer4);
    MessageBox.Show("Count 2:" + count2 + ", Equals: " + equals);
}
Output:
Count 1: 4
Count 2: 6, Equals: True

Тест 2 - Справочный счет cont.

private static void Test2(_Application outlookApp)
{
    var explorer1 = outlookApp.ActiveExplorer();
    var count1 = Marshal.ReleaseComObject(explorer1);
    MessageBox.Show("Count 1:" + count1);

    var explorer2 = outlookApp.ActiveExplorer();

    var explorer3 = explorer2 as _Explorer;
    var explorer4 = (ExplorerEvents_10_Event)explorer2;
    var explorerObject = (object)explorer2;
    var explorer5 = (Explorer)explorerObject;

    var equals = explorer2 == explorer3 && ReferenceEquals(explorer2, explorer5);
    var count2 = Marshal.ReleaseComObject(explorer4);
    MessageBox.Show("Count 2:" + count2 + ", Equals: " + equals);
}
Output:
Count 1: 4
Count 2: 4, Equals: True

Источники, на которых я передаю в дополнение к своему опыту и тестированию:

1. Йоханнесс прохождение - правила подсчета справочника RCW!

2. Эран Сэндлер - внутренние внутренние внутренности оболочки и общие ловушки во время выполнения

3. Эран Сэндлер - маршал. Releasecomobject и CPU Spinning

4. MSDN - Callible Callebure во время выполнения

Другие советы

Я не видел кода для RCW - даже не уверен, что он является частью SSCLI - но мне пришлось внедрить аналогичную систему для отслеживания времени жизни объекта в Slimdx и пришлось провести немало исследований в RCW. Это то, что я помню, надеюсь, это достаточно точно, но возьмите это с оттенком соли.

Когда система сначала видит указатель интерфейса COM, она просто идет в кеш, чтобы увидеть, есть ли RCW для этого указателя интерфейса. Предположительно, кэш будет использовать слабые ссылки, чтобы не предотвратить завершение и сбор RCW.

Если для этого указателя есть живая обертка, система возвращает обертку - если интерфейс был получен таким образом, который увеличил количество ссылок на интерфейс, предположительно, система RCW вызовет выпуск () на этом этапе. Он нашел живую обертку, поэтому знает, что обертка - это единственная ссылка, и она хочет поддерживать ровную ссылку. Если в кэше нет живой обертки, она создает новую и возвращает ее.

Вызывает вызовы обертки в основном указателе интерфейса COM из финализатора.

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

К сожалению, у меня нет хорошего понимания того, как система RCW обрабатывает генерацию прокси -объектов для отправки вещей через домены приложений или квартиры с потоком; Это не был аспект системы, который мне нужно было копировать для Slimdx.

Вам не нужно никакого особого лечения. Средство выполнения сохраняет только одну ссылку на объект COM. Причиной этого является то, что GC отслеживает все управляемые ссылки, поэтому, когда RCW выходит из сферы действия и собирается, выпускается ссылка на COM. Когда вы передаете управляемую ссылку, GC отслеживает его для вас - это одно из самых больших преимуществ работы на основе GC по сравнению с старой схемой AddRef/выпуска.

Вам не нужно вручную звонить в Marshal.ReleAseComObject, если вы не хотите более детерминированного выпуска.

А принятое решение действителен, но вот некоторая дополнительная справочная информация.

RCW содержит одну или несколько нативных ссылок на объект COM внутренне для своего объекта COM.

Когда RCW выпускает свой основной объект COM, либо из -за собранного мусора, либо из -за Marshal.ReleaseComObject() Получив его, он выпускает все свои внутренние объектные интерфейсы Com.

Здесь на самом деле есть много ссылок - один из них определяет, когда RCW .NET должен выпустить свои базовые интерфейсы COM -объекта, и тогда каждый из этих необработанных интерфейсов имеет свое собственное количество ссылок, как и в обычном ком.

Вот код, чтобы получить Raw Com IUnknown Ссылка на интерфейс:

int getIUnknownReferenceCount(object comobject)
{
    var iUnknown = Marshal.GetIUnknownForObject(comObject);
    return Marshal.Release(iUnknown);
}

И вы можете получить то же самое для других компонентов COM объекта, используя Marshal.GetComInterfaceForObject().

В дополнение к способам, перечисленным в принятое решение, мы также можем увеличить количество ссылок .NET RCW искусственно, позвонив что -то вроде Marshal.GetObjectForIUnknown().

Вот пример кода, используя эту технику, чтобы получить данное количество справочных ссылок COM объекта:

int comObjectReferenceCount(object comObject)
{
    var iUnknown = Marshal.GetIUnknownForObject(comObject);
    Marshal.GetObjectForIUnknown(iUnknown);
    Marshal.Release(iUnknown);
    return Marshal.ReleaseComObject(comObject);
}

Вам нужно позвонить Marshal.ReleaseComObject на вашей переменной WRD, чтобы выпустить ссылку на приложение Word.

Таким образом, если слово не видно, и вы закрываете свое приложение, EXE также будет разгружаться, если вы не сделаете его видимым для пользователя.

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