2009-06-04 37 views
12

Các đối tượng COM thường có sự hủy diệt xác định: chúng được giải phóng khi tham chiếu cuối cùng được giải phóng.C# + COM Interop, xác định phát hành

Cách này được xử lý trong C# - COM Interop? Các lớp không thực hiện IDisposable, vì vậy tôi thấy không có cách nào để kích hoạt một IUnknown :: Release.

Một thử nghiệm bình thường cho thấy rằng các đối tượng COM không được quan tâm được thu thập một cách lười biếng (tức là bộ thu gom rác đang kích hoạt bản phát hành). Tôi nên làm gì cho các đối tượng OCM cần được giải phóng một cách hăng hái? (ví dụ: nắm giữ các tài nguyên quan trọng lớn hoặc được chia sẻ)?

Vấn đề ban đầu: Chúng tôi có một ứng dụng C# sử dụng rất nhiều thư viện COM, và nó bị rò rỉ như điên. Có vẻ như các vấn đề là "giữa" C++ và mã C# (chúng ta có quyền truy cập vào cả hai), nhưng chúng ta không thể đinh nó xuống.

Trả lời

16

Bạn có thể thao tác các tham chiếu interop COM bằng cách sử dụng lớp System.Runtime.InteropServices.Marshal. Cụ thể bạn có thể muốn xem Marshal.ReleaseComObject.

+2

+1, bởi vì đây đã cứu sống tôi nhiều hơn một lần. – OregonGhost

5

Chúng tôi đã gặp phải điều này khá nhiều. Tốt nhất là không nên tải quá nhiều tham chiếu interop vào thời gian chạy .Net. Ngoài ra, bạn có thể sử dụng API Marshal.ReleaseComObject nếu bạn cần phát hành một thứ gì đó ngay lập tức.

Một phương pháp tốt khác là cấu trúc lại mã khách hàng của bạn để sử dụng trình bao bọc kiểu xung quanh mã interop - nếu bạn có một tham chiếu đã biết trong mã của bạn cho từng và mọi RCW interop, điều này làm tăng cơ hội tham chiếu interop sẽ được GC trong một cách kịp thời. Vấn đề chính này cố gắng tránh là một trong những "quá nhiều dấu chấm":

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

Mỗi phòng trong số các đối tượng giữa các chấm trong đoạn code trên được rò rỉ một cách hiệu quả (có thể là không thực sự trong thời gian dài) vì chúng ta có một tham chiếu ngầm định đối với cá thể. Bạn sẽ cần phải tạo tài liệu tham khảo đặt tên cho mỗi trường hợp để có một cơ hội tốt để làm sạch chúng lên:

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

Bây giờ có thể sử dụng thời gian chạy để phát hành tài liệu tham khảo:

ReleaseComObject(xyzzy); // etc... 
2

Đây là từ một số related (but subtly different) question, nhưng tôi nghĩ câu trả lời khá gọn gàng - vì vậy tôi nghĩ nó cũng đảm bảo thêm ở đây nữa.

Dưới đây là một lựa chọn có sử dụng Expression cây để thảo luận về ý định của chúng tôi, nắm bắt được giá trị tại mỗi nút - cho phép phát hành duy nhất:

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(); 
    } 
} 
Các vấn đề liên quan