C#+ COM Interop,确定性发布

COM对象通常具有确定性破坏:在释放最后一个引用时释放它们。

如何处理C# – COM Interop? 这些类没有实现IDisposable ,所以我认为无法触发显式的IUnknown :: Release。

随意测试显示未引用的COM对象被懒惰地收集(即垃圾收集器触发释放)。 我应该怎样对需要被激活释放的OCM对象做什么? (例如持有大型或共享的关键资源)?

原始问题:我们使用COM库大量使用C#应用程序,并且它像疯了一样泄漏。 似乎问题是“在C ++和C#代码之间”(我们可以同时访问它们),但我们无法确定它。

您可以使用System.Runtime.InteropServices.Marshal类操作COM互操作引用。 具体来说,您可能需要查看Marshal.ReleaseComObject 。

我们遭受了这么多苦难。 最好不要尝试在.Net运行时加载太多的互操作引用。 此外,如果您需要立即发布内容,可以使用Marshal.ReleaseComObject API。

另一个好的方法是重构您的客户端代码以使用互操作代码周围的类型安全包装器 – 如果您的代码中有一个已知的引用到每个互操作RCW,这将增加互操作引用及时GC的机会。 这个想要避免的主要问题是“太多点”:

 foo.bar.quux.xyzzy.groo(); // where foo, bar, quux and xyzzy are all COM references 

上面代码中点之间的每个对象都被有效泄露(可能不是长期运行),因为我们有一个对实例的隐式引用。 您需要为每个实例创建命名引用,以便有机会清理它们:

 Foo foo; Bar bar=foo.bar; Quux quux=bar.quux; Xyzzy xyzzy=quux.xyzzy; xyzzy.groo(); 

现在可能使用运行时来释放引用:

 ReleaseComObject(xyzzy); // etc... 

这是来自一个相关(但略有不同)的问题 ,但我认为答案非常整洁 – 所以我认为有必要在这里添加。

这是一个选项,它使用Expression树来讨论我们的意图,捕获每个节点的值 – 允许单个版本:

 static class ComExample { static void Main() { using (var wrapper = new ReleaseWrapper()) { var baz = wrapper.Add( () => new Foo().Bar.Baz ); Console.WriteLine(baz.Name); } } } class ReleaseWrapper : IDisposable { List objects = new List(); public T Add(Expression> func) { return (T)Walk(func.Body); } object Walk(Expression expr) { object obj = WalkImpl(expr); if (obj != null && Marshal.IsComObject(obj) && !objects.Contains(obj)) { objects.Add(obj); } return obj; } object WalkImpl(Expression expr) { switch (expr.NodeType) { case ExpressionType.Constant: return ((ConstantExpression)expr).Value; case ExpressionType.New: NewExpression ne = (NewExpression)expr; object[] args = ne.Arguments.Select(arg => Walk(arg)).ToArray(); return ne.Constructor.Invoke(args); case ExpressionType.MemberAccess: MemberExpression me = (MemberExpression)expr; object target = Walk(me.Expression); switch (me.Member.MemberType) { case MemberTypes.Field: return ((FieldInfo)me.Member).GetValue(target); case MemberTypes.Property: return ((PropertyInfo)me.Member).GetValue(target, null); default: throw new NotSupportedException(); } default: throw new NotSupportedException(); } } public void Dispose() { foreach(object obj in objects) { Marshal.ReleaseComObject(obj); Debug.WriteLine("Released: " + obj); } objects.Clear(); } }