C# + COM Interop, deterministic release

后端 未结 3 578
旧时难觅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 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();
        }
    }
    
        

    提交回复
    热议问题