我正在寻找管理在 .Net 程序集中创建的 COM 对象生命周期的想法,然后将这些对象传递回非托管代码。
背景: 我们的软件引擎是用非托管 C++ 编码的。我们的软件的功能可以使用引擎的 COM 对象组件进行扩展。在大多数情况下,发动机本身多年来都不需要更换。然而,我们继续构建更多组件来为我们的软件添加新功能。虽然这些组件过去也是用非托管 C++ 构建的,但在过去几年中,我们一直在用 C# 编写这些插件。
直到最近,我们都同意这样的想法:不应在 C# 组件中使用的非托管 COM 对象上调用 Marshal.ReleaseComObject,而应允许 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 释放其引用,我们能想到的唯一想法是重新工作引擎,以便所有接口(interface)都有一个 Cleanup() 调用有时,但这会很耗时,而且在某些情况下实现起来相当麻烦。
tl;dr 有没有办法强制 RCW 释放其对返回到非托管环境中的 COM 对象的引用?
最佳答案
but in practice we have begun having memory issues with our software
这是一个典型的成长痛问题。软件永远不会变小,团队中没有人因编写负面代码而获得奖励。添加越来越多的功能,您的程序永远不会使用更少的内存。标准诊断是消耗第一个 GB 的虚拟内存空间需要很长时间。第二个千兆字节消失得很快。
您现在所处的位置非常效率低下,您正在为几兆字节而烦恼。只需将开关拨至“没有问题”即可。将 native 代码编译为 64 位。您不会从托管代码中得到任何反对意见,x64 抖动已经知道如何在不进行任何更改的情况下做到这一点。
关于.net - Marshal.ReleaseComObject 和返回值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17373500/