Как управлять временем выполнения COM-объектов в стиле RAII в программах на C #? - PullRequest
3 голосов
/ 08 августа 2011

Моя программа на C # использует COM-компонент, который имеет множество различных интерфейсов и подобъектов.Проблема заключается в том, что каждый раз, когда я получаю какой-либо COM-интерфейс, создается RCW и этот RCW существует в течение неизвестного времени (пока не будет собран GC).Каждый RCW содержит некоторый объект на COM-сервере.

COM-компонент является сервером out-proc, поэтому его объекты находятся в отдельном довольно тяжелом процессе, который не завершится, пока все объекты, находящиеся в нем, не будутвышел.Я хочу, чтобы все объекты выпускались как можно скорее, чтобы я точно знал, что после того, как я выпустил последний объект, процесс сервера out-proc завершается и больше не потребляет системные ресурсы.Это не моя паранойя - наличие нескольких тяжеловесных процессов, потребляющих системные ресурсы, особенно память, действительно сказывается на производительности.

До сих пор я создавал универсальный класс, который реализует IDisposable, принимает ссылку на объект (RCW вфакт) и вызывает Marshal.FinalReleaseComObject в Dispose реализации.

Ради этого вопроса давайте временно проигнорируем возможные проблемы, возникающие при использовании FinalReleaseComObject() - я знаю о них иЯ могу терпеть их.

Реальная проблема в том, что я вынужден либо использовать using для всех объектов, либо писать finally предложения и вызывать Dispose() там.Это работает, но код становится довольно загроможденным из-за большого количества дополнительной проводки, и это беспокоит меня.

Например, мне нужно присвоить строку свойству someObject.Params.ColorParams.Hint - я не могу просто написать

someObject.Params.ColorParams.Hint = whatever;

потому что для каждого средства доступа будет COM-объект, который мне нужно освободить, поэтому вместо этого у меня есть:

using( MyWrapper<IParams> params = new MyWrapper<IParams>( someObject.Params ) ) {
    using( MyWrapper<IColorParams> colorParams =
         new MyWrapper<IColorParams>( params.Controlled ) )
    {
        colorParams.Controlled.Hint = whatever;
    }
}

, и это самый тривиальный пример - иногда мне нужно получить доступчто-то глубиной в пять уровней, а затем я пишу набор using операторов глубиной в пять уровней.

Есть ли более элегантное решение проблемы?

Ответы [ 3 ]

1 голос
/ 06 октября 2016

Пожалуйста, посмотрите этот ответ, чтобы очистить объекты взаимодействия Excel с IDisposable Хансом Пассантом.

Таким образом, вам не нужно IDisposable вообще. Просто явно вызовите сборку мусора после , все ваши ссылки COM вышли из области видимости, то есть не из функции, которая их использует, а из вызывающей стороны этой функции.

0 голосов
/ 08 августа 2011

c # и детерминированное освобождение ресурсов обычно в лучшем случае проблематично.
Освобождение внепроцессных COM-объектов является одним из примеров этой проблемы.

Если вы счастливы написать некоторый код C ++ / cli, у него есть опция семантики стека для управляемых объектов кучи. Это исключает необходимость использования или окончательного переноса, поскольку вызовы компилятора удаляют вас, когда объект выпадает из области видимости.

Конечно, это приносит вам более шумный язык, чем c #, заголовочные файлы., ->, :: и т. Д.

Как правило, если ваш код способен создавать и освобождать COM-объекты в локальной области внутри блока using, я бы остался в C # и мирился с проблемой использования.

Если вашему коду необходимо сохранить COM-объекты в качестве переменных-членов, я бы переместил только эти классы в c ++ / cli и использовал бы семантику стека для переменных-членов, которые автоматически будут связывать вызовы для вас.

0 голосов
/ 08 августа 2011

Я не уверен, что полностью понимаю проблему, но если я это сделаю (беспорядок в коде), есть 2 возможных решения:

Метод обертки - Вы можете определить простой метод:

TResult Exec<TResult>(Func<TResult> func, params MarshalByRefObject comObjects)
{
  TResult res = default(TResult);
  try
  {
    res = func.Invoke();
  }
  catch (Exception e) { /* Log? */ }
  finally
  {
    foreach (MarshalByRefObject combObj in comObjects)
    {    /* release resources using common interface or reflection */ }
  }
}

Очень похоже на предыдущий метод, для этого используется только одна из множества сред AOP (например, PostSharp )

...