Я ищу идеи, управляющие временем жизни COM-объектов, созданных в .NET-сборке, которые затем передаются обратно в неуправляемый код.Marshal.ReleaseComObject и возвращаемые значения
Фон: Наш программный движок закодирован в неуправляемом C++. Функциональность нашего программного обеспечения расширяется с использованием компонентов COM-объекта для движка. По большей части сам двигатель не нужно менять годами. Однако мы продолжаем создавать дополнительные компоненты для добавления новых функций в наше программное обеспечение. Хотя эти компоненты также были созданы в неуправляемом C++, в течение последних двух лет мы кодировали эти плагины на C#.
До недавнего времени мы подписались на идею Marshal.ReleaseComObject не следует вызывать на неуправляемых объектах COM, используемых в компонентах C#, и вместо этого разрешить сборку RCW и мусора управлять своей жизнью.
Эта теория звучит неплохо, но на практике у нас начались проблемы с памятью с нашим программным обеспечением, которое, по-видимому, вызвано тем, что GC ленив по очистке этих COM-объектов до такой степени, что в некоторых случаях наше программное обеспечение будет использовать все доступные памяти и сбоя.
Есть ли какая-то ошибка, которую мы могли бы сделать, что мешает GC и RCW действовать на этих объектах? Разве идея сбора мусора не означает, что она будет делать то, что необходимо, чтобы обеспечить свободный доступ к памяти при необходимости?
Из-за отсутствия лучших идей мы неохотно начали использовать Marshal.ReleaseComObject (и в некоторых случаях FinalReleaseComObject). В общем, это похоже на трюк.
Однако, пытаясь разработать согласованный шаблон для использования Marshal.ReleaseComObject, мы обнаружили один случай, в котором мы не уверены, что его можно использовать. Некоторым компонентам необходимо передать возвращаемые значения объекта COM обратно в неуправляемый движок. Компонент никогда больше не увидит возвращаемое значение и (в настоящее время) не может быть уведомлен, когда он больше не используется. Например:
// Unmanaged C++ code
Engine::Engine() : m_ipDoodadCreator(CLSID_FancyComponent)
{
}
void Engine::ProcessDoodad()
{
try
{
// Smart pointer
IDoodadPtr ipDoodad = m_ipDoodadCreator->CreateDoodad();
...
ipDoodad = NULL; // Calls Release...
}
catch (...) { ... }
// At this point the doodad lives on because the RCW is holding a
// reference to it.
}
// Managed C# Component
public class FancyComponent : IDoodadCreator
{
public IDoodad IDoodadCreator.CreateDoodad()
{
// IDoodad is implmented in unmanaged c++
IDoodad doodad = new IDoodad();
try
{
...
return doodad;
}
finally
{
// Can't do this here because engine doesn't yet have
// a reference to doodad.
// Marshal.ReleaseComObject(doodad);
}
}
}
До сих пор единственная идея, которую мы можем придумать, чтобы детерминировано Получить RCW выпустить ссылку в этой ситуации было бы переработать двигатель так, что все интерфейсы имеют Cleanup(), вызываемый время от времени, но это потребует много времени и, в некоторых случаях, довольно громоздко реализовать.
tl; dr Есть ли способ заставить RCW освободить ссылку на объект COM, который возвращается в неуправляемую среду?
'IDoodad' должен наследовать от' IUnknown', поэтому вы должны иметь возможность вызывать 'Release', как правило, последний, который я проверил. – Mgetz
Release уменьшает счетчик ссылок. Если что-то еще (RCW) все еще имеет ссылку, doodad продолжает жить. В приведенном выше коде я подразумеваю, что IDoodadPtr - это умный указатель, вызывающий Release, когда он выходит за рамки. – Alias
Вы имеете в виду [COM Callable Wrapper] (http://msdn.microsoft.com/en-us/library/f07c8z1c.aspx)? согласно MSDN, если релиз называется обычно, он освободит объект для сбора мусора. – Mgetz