C#+ COM Interop,确定性发布

时间:2009-06-04 08:06:24

标签: c# com interop

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

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

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

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

3 个答案:

答案 0 :(得分:16)

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

答案 1 :(得分:5)

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

另一个好的方法是重构您的客户端代码以使用互操作代码周围的类型安全包装器 - 如果您的代码中有一个已知的引用到每个互操作RCW,这就增加了互操作引用在GC中的GCed的可能性。及时时尚。这个试图避免的主要问题是“太多点”:

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...

答案 2 :(得分:2)

这是来自related (but subtly different) question,但我认为答案非常整洁 - 所以我认为有必要在此添加。

这是一个使用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<object> objects = new List<object>();
    public T Add<T>(Expression<Func<T>> 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();
    }
}