2012-04-11 18 views
7

Tengo una clase en mi aplicación .NET 3.5 C# WinForms que tiene cinco métodos. Cada método utiliza diferentes conjuntos de interfaces COM de C++. Estoy usando Marshal.FinalReleaseCOMObject para limpiar estos objetos COM. Este código funciona bien en esta plataforma .NET sin ningún problema. Pero cuando muevo esta aplicación para .NET 4.0, comienzo a conseguir este error en uno de estos métodos en una línea donde yo echo una variable ICOMInterface1-ICOMInterface2, es decir:"El objeto COM que se ha separado de su RCW subyacente no se puede usar" con .NET 4.0

ICOMInterface1 myVar= obj as ICOMInterface2; 

objeto COM que tiene separado de su RCW subyacente no puede ser usado.

Y si elimino la línea donde estoy usando Marshal.FinalReleaseCOMObject, no obtengo este error.

¿Qué me falta aquí? ¿Y cómo puedo limpiar estos objetos COM no administrados desde la memoria en la plataforma .NET 4.0?

Respuesta

13

La respuesta simple es nunca use Marshal.FinalReleaseComObject a menos que sea absolutamente necesario. Y si lo hace, hay algunas reglas adicionales que debe seguir.

Cuando se usa un objeto COM en .NET, el tiempo de ejecución crea lo que se conoce como "RCW" o "envoltorio invocable en tiempo de ejecución" para ese objeto. Este RCW es simplemente un objeto normal que contiene una referencia COM en el objeto. Cuando este objeto es basura recolectada, llamará a IUnknown :: Release() en el objeto COM, como era de esperar. Lo que esto significa es que a menos que su objeto COM requiera que la última versión() se realice en un momento muy determinado, simplemente deje que el recolector de basura se encargue de ello. Muchos objetos COM se incluyen en este caso, por lo tanto, asegúrese de que debe administrar la llamada a Release() cuidadosamente.

Así que cuando llamas a FinalReleaseComObject, eso esencialmente disminuye la referencia que el RCW tiene en el objeto COM hasta que llega a cero y el RCW luego libera el objeto COM. En este punto, este RCW ahora está zombie, y cualquier uso de él dará la excepción que has visto. El CLR (de forma predeterminada) solo crea un único RCW para cualquier objeto COM subyacente, por lo que esto significa que si la API COM que está utilizando devuelve el mismo objeto dos veces, solo tendrá un solo RCW. Llamar a FinalReleaseComObject significaría que de repente todos los usos de de ese RCW son tostados.

La única manera de garantizarle que tiene un Marshal.GetUniqueObjectForIUnknown exclusivo, que impide cualquier intercambio de RCW. Pero como dije antes, en la mayoría de las API COM esto no es necesario, en primer lugar, así que no lo hagas.

Paul Harrington escribió una buena blog post sobre [Final] ReleaseComObject y sus males. Es un arma peligrosa que a menos que sea necesaria solo te lastimará. Ya que estás haciendo esta pregunta, sospecho que en realidad no necesitas llamarla para nada. :-)

+0

Gracias a Jason por sus entradas. ¿Esta explicación también es válida para el método ReleaseCOMObject? Porque, cuando la uso en lugar de FinalReleaseCOMObject, todavía veo el mismo comportamiento ... es decir, funciona en 3.5 pero no en 4.0. Además, me preguntaba qué tenía que ver la versión de .NET Framework con este comportamiento? ¿Es porque la forma en que funciona GC ahora está modificada en 4.0? – user74042

+1

Por lo tanto, los RCW tienen un recuento que se reduce cuando se llama a Marshal.ReleaseComObject. FinalReleaseComObject solo lo llama hasta que el refcount llegue a cero. –

+1

En cuanto a qué cambió en el CLR que causó esto ...es difícil de decir sin un depurador. –

Cuestiones relacionadas