我正在寻找管理在 .Net 程序集中创建的 COM 对象的生命周期的想法,然后将这些对象传递回非托管代码。
背景:我们的软件引擎是用非托管 c++ 编码的。我们软件的功能可以使用引擎的 COM 对象组件进行扩展。在大多数情况下,引擎本身多年来不需要改变。但是,我们将继续构建更多组件以向我们的软件添加新功能。虽然这些组件过去也是用非托管 C++ 构建的,但在过去的几年里,我们一直在用 C# 编写这些插件。
直到最近,我们才同意 Marshal.ReleaseComObject 不应该在 C# 组件中使用的非托管 COM 对象上调用的想法,而是允许 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 对象的引用?