2009-06-04 68 views
12

COM對象通常具有確定性破壞:當最後一個引用被釋放時,它們被釋放。C#+ COM Interop,確定性發佈

在C#中如何處理?COM Interop?這些類沒有實現IDisposable,所以我看不到觸發顯式IUnknown :: Release的方法。

一個臨時測試表明,懶惰地收集未引用的COM對象(即垃圾收集器正在觸發釋放)。我應該怎樣做OCM對象,需要進行積極釋放? (例如擁有大量或共享的關鍵資源)?

原始問題:我們有一個C#應用程序大量使用COM庫,並且它像瘋了一樣泄漏。看來問題出在C++和C#代碼之間(我們可以同時訪問這兩個代碼),但我們不能確定它。

回答

16

您可以使用System.Runtime.InteropServices.Marshal類操作COM互操作引用。具體來說,你可能想看看Marshal.ReleaseComObject

+2

+1,因爲這救了我的命不止一次。 – OregonGhost 2009-06-04 08:18:25

5

我們已經遭受了很多。最好不要嘗試將太多的互操作引用加載到.Net運行時。另外,如果您需要立即發佈某些東西,則可以使用Marshal.ReleaseComObject API。

另一個好方法是重構你的客戶端代碼以在互操作代碼周圍使用類型安全包裝 - 如果你的代碼中有對每個互操作RCW的已知參考,這就增加了互操作引用將被GCed及時的方式。此舉旨在避免的主要問題是「太多了點」之一:

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

每個在上面的代碼點之間的對象的有效泄漏(可能不是真的,從長遠來看),因爲我們有一個隱式引用該實例。您需要纔能有一個很好的機會來清除它們創建到每個實例的命名參考:

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

現在可能使用運行時釋放參考:

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

這是從related (but subtly different) question,但我認爲答案是非常整潔 - 所以我認爲它也有必要加入。

下面是一個使用Expression樹木,討論我們的意圖,在每個節點捕捉價值選項 - 允許一個單一版本:

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