C# + COM Interop, deterministic release

后端 未结 3 567
旧时难觅i
旧时难觅i 2020-12-30 07:20

COM objects usually have deterministic destruction: they are freed when the last reference is released.

How is this handled in C# - COM Interop? The classes don\'t

相关标签:
3条回答
  • 2020-12-30 07:52

    This is from a related (but subtly different) question, but I think the answer is pretty tidy - so I thought it warranted adding here too.

    Here's an option that uses Expression trees to discuss our intent, capturing the value at each node - allowing a single release:

    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();
        }
    }
    
    0 讨论(0)
  • 2020-12-30 08:00

    We've suffered from this quite a lot. It's best not to try to load too many interop references into the .Net runtime. Also, you can use the Marshal.ReleaseComObject API if you need to release something right away.

    Another good method is to refactor your client code to use typesafe wrappers around the interop code - if you have a known reference in your code to each and every interop RCW, this increases the chance that the interop reference will be GCed in a timely fashion. The main problem this seeks to avoid is the one of "too many dots":

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

    Each of the objects between dots in the above code is effectively leaked (probably not really in the long run) since we have an implicit reference to the instance. You would need to create named references to each of the instances in order to have a good chance to clean them up:

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

    Now possibly use the runtime to release the reference:

    ReleaseComObject(xyzzy); // etc...
    
    0 讨论(0)
  • 2020-12-30 08:09

    You can manipulate COM interop references using the System.Runtime.InteropServices.Marshal class. Specifically you may want to have a look at Marshal.ReleaseComObject.

    0 讨论(0)
提交回复
热议问题