Pregunta

Tengo una aplicación que utiliza los conjuntos de interoperabilidad de Office. Soy consciente de la "tiempo de ejecución (RCW)" gestionado por el tiempo de ejecución. Pero no estoy muy seguro de cómo el contador de referencias se incrementa. MSDN dice:

RCW mantiene sólo una referencia a la envuelta objeto COM independientemente de la número de clientes administrados llamándolo.

Si he entendido bien, en el siguiente ejemplo:

using Microsoft.Office.Interop.Word;

static void Foo(Application wrd)
{
    /* .... */
}

static void Main(string[] args)
{
    var wrd = new Application();
    Foo(wrd);
    /* .... */
}

Estoy pasando el wrd ejemplo a otro método. Pero esto no incrementa el recuento de referencia interna. Así que me pregunto en qué escenarios el recuento de referencia se incrementa? Puede señalar a nadie fuera de un escenario en el que el contador de referencias se incrementa?

También he leído algunos blog que dice evitar el uso de dobles puntos en la programación con objetos COM. Algo así como, wrd.ActiveDocument.ActiveWindow. El autor afirma que el compilador crea variables separadas para contener los valores que se incrementa el contador de referencia. En mi humilde opinión, esto es un error y el primer ejemplo demuestra. ¿Es eso correcto?

Cualquier ayuda sería grande!

¿Fue útil?

Solución

He estado investigando esta cuestión también, trabajando en una aplicación COM / Net-Interop-céntrico, la lucha contra las fugas, se cuelga y se estrella.

Respuesta corta: Cada vez que el objeto COM se pasa de entorno COM a .NET

.

Respuesta larga:

  1. Para cada objeto COM hay un objeto RCW [Test 1] [Ref 4]
  2. recuento de referencia se incrementa cada vez que se solicita el objeto desde dentro de objeto COM (llamando propiedad o un método en el objeto COM que objeto retorno COM, el recuento de referencia de objeto COM devuelto se incrementa en uno) [Test 1]
  3. recuento
  4. Referencia no se incrementa por colada a otras interfaces COM del objeto o mover la referencia RCW alrededor [Test 2]
  5. recuento de referencia se incrementa cada vez que un objeto se pasa como un parámetro en caso planteado por COM [Ref 1]

En una nota: Usted debe Siempre COM liberación objetos tan pronto como haya terminado de usarlos. Dejando esta labor a la GC puede dar lugar a fugas, un comportamiento inesperado y bloqueos de eventos. Esto es diez veces más importante si el acceso a objetos no en el subproceso STA que se creó el. [Ref 2] [Ref 3] [experiencia personal dolorosa]

Estoy esperanza he cubierto todos los casos, pero COM es un hueso duro de roer. Saludos.

Prueba 1 - Referencia recuento

private void Test1( _Application outlookApp )
{
    var explorer1 = outlookApp.ActiveExplorer();
    var count1 = Marshal.ReleaseComObject(explorer1);
    MessageBox.Show("Count 1:" + count1);

    var explorer2 = outlookApp.ActiveExplorer();
    var explorer3 = outlookApp.ActiveExplorer();
    var explorer4 = outlookApp.ActiveExplorer();

    var equals = explorer2 == explorer3 && ReferenceEquals(explorer2, explorer4);
    var count2 = Marshal.ReleaseComObject(explorer4);
    MessageBox.Show("Count 2:" + count2 + ", Equals: " + equals);
}
Output:
Count 1: 4
Count 2: 6, Equals: True

Prueba 2 -. Recuento de referencia cont

private static void Test2(_Application outlookApp)
{
    var explorer1 = outlookApp.ActiveExplorer();
    var count1 = Marshal.ReleaseComObject(explorer1);
    MessageBox.Show("Count 1:" + count1);

    var explorer2 = outlookApp.ActiveExplorer();

    var explorer3 = explorer2 as _Explorer;
    var explorer4 = (ExplorerEvents_10_Event)explorer2;
    var explorerObject = (object)explorer2;
    var explorer5 = (Explorer)explorerObject;

    var equals = explorer2 == explorer3 && ReferenceEquals(explorer2, explorer5);
    var count2 = Marshal.ReleaseComObject(explorer4);
    MessageBox.Show("Count 2:" + count2 + ", Equals: " + equals);
}
Output:
Count 1: 4
Count 2: 4, Equals: True

Fuentes I relé en, además de mi experiencia y pruebas:

1. Johannes de Passing - reglas de conteo RCW Referencia = Saldo de referencias COM Reglas

2. Eran Sandler - tiempo de ejecución se puede llamar la envoltura de funcionamiento interno y las trampas comunes

3. Eran Sandler - Marshal.ReleaseComObject y CPU Spinning

4. MSDN - Tiempo de ejecución rescatable Envoltura

Otros consejos

No he visto el código de la RCW - ni siquiera seguro de que es parte de la SSCLI - pero tenía que implementar un sistema similar para el seguimiento de duración de los objetos COM en SlimDX y tuvo que hacer un poco de investigación en RCW. Esto es lo que recuerdo, es de esperar que es bastante exacto, pero lo tomo con un toque de sal.

Cuando el sistema ve por primera vez un puntero de interfaz COM, que sólo sirve para una caché para ver si hay un RCW para ese puntero de interfaz. Es de suponer que la memoria caché estaría utilizando referencias débiles, por lo que no se impida la finalización y la recogida de la RCW.

Si hay un envoltorio en vivo para ese puntero, el sistema devuelve el envoltorio - si se obtuvo la interfaz de una manera que incrementa el contador de referencia de la interfaz, presumiblemente el sistema RCW llamaría Release () en este punto. Se ha encontrado un envoltorio en vivo, por lo que sabe que la envoltura es una referencia única y quiere mantener exactamente una referencia. Si no hay un envoltorio en vivo en la memoria caché, se crea uno nuevo y lo devuelve.

La envoltura de llama de prensa sobre la interfaz COM puntero subyacente (s) del finalizador.

La envoltura se encuentra entre usted y el objeto COM, y se ocupa de todo cálculo de referencias de parámetros. Esto también permite que ésta pueda asumir el resultado prima de cualquier método de interfaz que es en sí mismo otro puntero de interfaz y ejecutar ese puntero a través del sistema RCW almacenamiento en caché para ver si existe aún antes de devolver el puntero de interfaz envueltos.

Por desgracia, no tienen una buena comprensión de cómo las manijas del sistema de generación de RCW objeto proxy para el envío de material a través de dominios de aplicación o apartamentos de hilo; que no era un aspecto del sistema que necesitaba para copiar SlimDX.

No debería ser necesario ningún tratamiento especial. El tiempo de ejecución sólo mantiene una referencia al objeto COM. La razón de esto es que el GC seguimiento de todas las referencias gestionadas, así que cuando el RCW sale del ámbito y se recoge, se libera la referencia COM. Cuando se pasa alrededor de una referencia gestionado, la GC es el seguimiento para usted -. Esta es una de las mayores ventajas de un tiempo de ejecución basado en la GC sobre el viejo esquema de AddRef / Liberación

no es necesario llamar manualmente Marshal.ReleaseComObject a menos que desee liberación más determinista.

La solución aceptada es válida, pero aquí hay algo de información adicional.

Un RCW contiene uno o más nativos de interfaz de referencias a objetos COM internamente por objeto COM.

Cuando un RCW libera su objeto COM subyacente, ya sea por conseguir basura recogida o debido a Marshal.ReleaseComObject() recibiendo llamadas en él, se libera de todas sus interfaces de objetos COM mantenidos internamente.

En realidad, hay muchos contadores de referencia aquí - uno de determinar cuándo .NET RCW debe liberar sus interfaces de objetos COM subyacentes, y luego cada una de esas interfaces COM primas tiene su propia cuenta de referencia como en COM regulares

.

Aquí hay código para obtener COM prima recuento de referencia de la interfaz IUnknown:

int getIUnknownReferenceCount(object comobject)
{
    var iUnknown = Marshal.GetIUnknownForObject(comObject);
    return Marshal.Release(iUnknown);
}

y se puede obtener el mismo para otras interfaces COM del objeto utilizando Marshal.GetComInterfaceForObject().

Además de las formas enumeradas en el solución aceptada, también podemos aumentar el número de referencia de .NET artificialmente por RCW llamar algo así como Marshal.GetObjectForIUnknown().

A continuación del código de ejemplo haciendo uso de esa técnica para conseguir contador de referencias RCW de un objeto COM dado:

int comObjectReferenceCount(object comObject)
{
    var iUnknown = Marshal.GetIUnknownForObject(comObject);
    Marshal.GetObjectForIUnknown(iUnknown);
    Marshal.Release(iUnknown);
    return Marshal.ReleaseComObject(comObject);
}

Es necesario llamar Marshal.ReleaseComObject en la variable de wrd para liberar su referencia a la aplicación de textos.

De esta forma si la palabra no es visible y se cierra la aplicación, el exe descargará así a menos que haya hecho que sea visible para el usuario.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top