2009-06-04 23 views
12

COM nesneleri genellikle deterministik yıkıma sahiptir: son referans bırakıldığında serbest bırakılırlar.C# + COM Etkileşim, deterministik serbestlik

Bu, C# - COM Interop'ta nasıl kullanılır? Sınıflar IDisposable'u uygulamıyor, bu yüzden açık bir IUnknown :: Release'i tetiklemenin hiçbir yolunu göremiyorum.

Sıradan bir test, ilgisiz COM nesnelerinin tembel bir şekilde toplanacağını gösterir (yani, çöp toplayıcı serbest bırakmayı tetikliyor). Agresif olarak salınması gereken OCM nesneleri için ne yapmalıyım? (ör. büyük veya paylaşılan kritik kaynakların tutulması)?

Asıl sorun: Bir COM kütüphanesi kullanarak yoğun bir C# uygulamasına sahibiz ve deli gibi sızdırıyor. Görünüşe göre problemler C++ ve C# kodları arasında (her ikisine de erişebiliyoruz) gözüküyor, ama biz onu ezemeyiz.

cevap

16

COM interop referanslarını System.Runtime.InteropServices.Marshal sınıfını kullanarak değiştirebilirsiniz. Özellikle Marshal.ReleaseComObject'a bakmak isteyebilirsiniz.

+2

+1: Tek bırakılmasını sağlayan -

İşte her düğüm noktasında değerini yakalayan, niyetimizi görüşmek üzere Expression ağaçları kullanan bir seçenek. – OregonGhost

5

Bu konuda çok acı çektik. .Net çalışma zamanına çok fazla birlikte çalışma referansı yüklememeye çalışmak en iyisidir. Ayrıca, bir şeyi hemen serbest bırakmanız gerekiyorsa, Marshal.ReleaseComObject API'sini kullanabilirsiniz.

Başka bir iyi yöntem, istemci kodunuzu birlikte çalışma kodunun etrafındaki kötü amaçlı sarmalayıcıları kullanacak şekilde yeniden kodlamaktır. Kodunuzda her birleşik RCW için bilinen bir başvurunuz varsa, bu, birlikte çalışma referansının GCed olması olasılığını artırır. zamanında moda. (Muhtemelen gerçekten uzun vadede) Elimizdeki beri yukarıdaki kodda noktalar arasındaki nesnelerin her biri etkin bir şekilde sızdırılmış

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

: Bunu önlemek istiyor asıl sorun "çok fazla nokta" biridir örneğe dolaylı başvuru.

Foo foo; 
Bar bar=foo.bar; 
Quux quux=bar.quux; 
Xyzzy xyzzy=quux.xyzzy; 
xyzzy.groo(); 
Şimdi muhtemelen başvurusunu serbest bırakmak için çalışma zamanını kullanmak

:

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

budur Onları temizlemek için iyi bir şans var için her örnekleri için adlandırılmış referans oluşturmak gerekir related (but subtly different) question dan, ama cevabın oldukça düzenli olduğunu düşünüyorum - bu yüzden de burada ekleyerek garanti olduğunu düşündüm. Bu kereden hayatım daha kurtardı çünkü

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(); 
    } 
}