2009-06-04 59 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(); 
    } 
}