临时释放COM对象对象、COM

2023-09-02 10:28:14 作者:有你才叫爱

使用COM对象考虑下面的C#code。

Consider the following C# code using a COM object.



MyComObject o = new MyComObject;
try
{
 var baz = o.Foo.Bar.Baz;
 try
 { 
  // do something with baz
 }
 finally
 {
  Marshal.ReleaseComObject(baz);
 }
}
finally
{
 Marshal.ReleaseComObject(o);
}

这将释放COM对象 0 巴兹,而不是由returnd临时对象 o.Foo o.Foo.Bar 。 这可能导致问题,当这些对象持有大量的非托管存储器或其他资源。

This will release the COM objects o and baz, but not the temporary objects returnd by o.Foo and o.Foo.Bar. This can cause problems, when those objects hold a large amount of unmanaged memory or other resources.

这是显而易见的,但丑陋的解决办法是,以更加与杂乱code尝试,终于 Marshal.ReleaseComObject的。看到 C#+ COM互操作的,确定性的释放

An obvious but ugly solution would be, to clutter the code even more with try-finally and Marshal.ReleaseComObject. See C# + COM Interop, deterministic release

作为一种变通方法,我创建了一个辅助类

As a workaround, I created a helper class



class TemporaryComObjects: IDisposable
{
 public C T<C>(C comObject)
 {
  m_objects.Add(comObject);
  return comObject;
 }
 public void Dispose()
 {
  foreach (object o in m_objects)
   Marshal.ReleaseComObject(o);
 }
}

用法:



using (TemporaryComObjects t = new TemporaryComObjects())
{
 MyComObject o = t.T(new MyComObject);
 var baz = t.T(t.T(t.T(o.Foo).Bar).Baz);
 // do something with baz
}

我的提问: 是否有与此code潜在的问题? 有没有人一个更好的解决方案?

My questions: Are there potential problems with this code? Has anybody a more elegant solution?

推荐答案

我最大的抱怨是名称, T ; 添加可能是使用了更illusrative。我还添加其中T:类的泛型方法,但一口流利的API似乎可用。我还倾向于扁平化℃的位$ C $。我还可以看到使用防爆pression API走整个目录树和捕获所有的中间步骤的一些方法,但它不会是的小事的 - 但是想象一下:

My biggest gripe would be the name, T; Add might be more illusrative of the usage. I'd also add where T : class to the generic method, but the "fluent API" seems usable. I'd also be inclined to flatten the code a bit. I can also see some ways of using the Expression API to walk an entire tree and capture all the intermediate steps, but it wouldn't be trivial - but imagine:

using(var com = new SomeWrapper()) {
    var baz = com.Add(() => new MyComObject().Foo.Bar.Baz);
}

其中,这是一个前pression树,我们会自动获得中介。

where that is an expression tree and we get the intermediaries automatically.

(也可以清除()的列表中处置( )

像这样:

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[] Walk(IEnumerable<Expression> args)
    {
        if (args == null) return null;
        return args.Select(arg => Walk(arg)).ToArray();
    }
    object WalkImpl(Expression expr)
    {
        switch (expr.NodeType)
        {
            case ExpressionType.Constant:
                return ((ConstantExpression)expr).Value;
            case ExpressionType.New:
                NewExpression ne = (NewExpression)expr;
                return ne.Constructor.Invoke(Walk(ne.Arguments));
            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();

                }
            case ExpressionType.Call:
                MethodCallExpression mce = (MethodCallExpression)expr;
                return mce.Method.Invoke(Walk(mce.Object), Walk(mce.Arguments));
            default:
                throw new NotSupportedException();
        }
    }
    public void Dispose()
    {
        foreach(object obj in objects) {
            Marshal.ReleaseComObject(obj);
            Debug.WriteLine("Released: " + obj);
        }
        objects.Clear();
    }
}