2009-06-04 18 views
12

Los objetos COM generalmente tienen destrucción determinística: se liberan cuando se lanza la última referencia.C# + COM Interop, versión determinística

¿Cómo se maneja esto en C# - COM Interop? Las clases no implementan IDisposable, por lo que no veo forma de activar un IUnknown :: Release explícito.

Una prueba casual muestra que los objetos COM sin referencia se recopilan de forma diferida (es decir, el recolector de elementos no utilizados desencadena la publicación). ¿Qué debo hacer para los objetos OCM que necesitan ser lanzados agresivamente? (por ejemplo, tener recursos críticos grandes o compartidos)?

Problema original: Tenemos una aplicación C# usando una biblioteca COM, y está goteando como loca. Parece que los problemas están "entre" el código C++ y el código C# (tenemos acceso a ambos), pero no podemos bloquearlo.

Respuesta

16

Puede manipular referencias de interoperabilidad COM utilizando la clase System.Runtime.InteropServices.Marshal. Específicamente, es posible que desee echar un vistazo a Marshal.ReleaseComObject.

+2

+1, porque esto me salvó la vida más de una vez. – OregonGhost

5

Hemos sufrido de esto bastante. Lo mejor es no intentar cargar demasiadas referencias de interoperabilidad en el tiempo de ejecución de .Net. Además, puede usar la API Marshal.ReleaseComObject si necesita liberar algo de inmediato.

Otro buen método es refactorizar su código de cliente para usar wrappers de tipo seguro alrededor del código de interoperabilidad; si tiene una referencia conocida en su código para cada interoperabilidad RCW, aumenta la probabilidad de que la referencia de interoperabilidad sea GCed una manera oportuna. El problema principal de este busca evitar es el de "demasiados puntos":

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

Cada uno de los objetos entre los puntos en el código anterior se filtró de manera efectiva (probablemente no es realmente en el largo plazo), ya que tenemos una referencia implícita a la instancia. Lo que se necesita para crear nominativo a cada uno de los casos con el fin de tener una buena oportunidad para limpiarlas:

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

Ahora, posiblemente, utilizar el tiempo de ejecución para liberar la referencia:

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

Este es de un related (but subtly different) question, pero creo que la respuesta es bastante ordenada, así que pensé que se justificaba añadir aquí también.

Aquí es una opción que utiliza Expression árboles para discutir nuestra intención, la captación del valor en cada nodo - permitir que un solo lanzamiento:

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