uso tag site qué manager gtag google fragmento contenedor almacena c# com interop proxy marshalling

c# - site - tag manager gtag



Llamar al componente administrado visible de COM desde un código administrado a través de un contenedor COM (7)

Tengo un componente de terceros, digamos FIPreviewHandler para manejar la vista previa, que implementa IPreviewHandler. FIPreviewHandler se implementa como un componente administrado y utiliza la interfaz IPreviewHandler y las interfaces relacionadas a través de una interoperabilidad. FIPreviewHandler se registra utilizando regasm.exe como COM.

Tengo una aplicación cliente que también es gestionada. Quiero crear una instancia de FIPreviewHandler como un componente COM en mi aplicación.

Tengo un ensamblaje de interoperabilidad que define IPreviewHandler y las interfaces relacionadas.

Cuando creo una instancia de FIPreviewHandler, utilizando Activator.CreateInstance (), en un tipo devuelto por GetTypeByCLSID (), que usa el CLSID correcto para FIPreviewHandler, me devuelve una instancia administrada, ya que tiene el ensamblaje real disponible y omite COM . Cuando intento QI / cast esta instancia como cualquiera de las interfaces, IPreviewHandler por ejemplo, devuelve nulo porque está cargado como un objeto administrado, y aunque la interfaz IPreviewHandler implementada por FIPreviewHandler es la misma interfaz que tengo en mi interoperabilidad , pero está en una diferencia de espacio de nombres / ensamblaje, por lo tanto nulo. Si tuviera que devolverme una instancia COM / RCW (Sistema .__ ComObject), no tomaría en cuenta el espacio de nombres, emitiría una multa y devolvería una instancia válida.

FIPreviewHandler es un componente de 32 bits, y en una máquina Win7 de 64 bits, si compilo mi aplicación cliente como "Cualquier CPU", Activator.CreateInstance () devuelve una instancia COM / RCW (System .__ ComObject), ya que no puede encontrar una implementación de 64 bits. de FIPreviewHandler, por lo tanto devuelve un proxy. En este escenario, mi aplicación funciona bien. Pero cuando lo compilo para x86, obtiene la implementación de 32 bits y devuelve una instancia administrada de la clase administrada real, y no una instancia COM, por lo tanto falla.

No puedo usar las interfaces definidas en el ensamblaje de FIPreviewHandler, ya que tengo que escribir un cliente genérico para IPreviewHandler, y mi aplicación funcionará con cualquier componente que implemente IPreviewHandler, lo que funcionaría muy bien para los clientes basados ​​en C ++ que acceden a FIPreviewHandler como un objeto COM, pero falla Para clientes gestionados.

Espero que tenga sentido y estaría muy agradecido por cualquier ayuda.


Claramente, esto es un error por parte de .NET, ya que estoy descubriendo que no hay manera de usar un contenedor COM alrededor de un objeto COM administrado.

La "solución" (y uso ese término muy libremente) es el uso de un PIA o "Ensamblaje de interoperabilidad primario". El PIA proporciona un único ensamblado con nombre seguro importado con TlbImp.exe que está registrado por GAC. Básicamente, supongo que luego debemos confiar en las políticas del editor de GAC para forzar a los clientes a que usen la interfaz correcta.

ver también

http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/b11a0f90-fcc5-487a-b057-632f5415bfc2

http://www.codeproject.com/KB/COM/BuildCOMServersInDotNet.aspx


He intentado hacer algo similar sin éxito. (En mi caso, la interfaz COM definida existente tenía un error que significaba que no podía implementarse en .Net, por lo que redefiní la interfaz COM en un espacio de nombres independiente e implementé esta interfaz en su lugar. El objeto resultante implementó la COM correcta. interfaces, sin embargo, no pude conseguir un contenedor COM para volver a la interfaz rota original).

Que yo sepa, solo hay 2 soluciones:

  1. Declare todas las interfaces COM en un ensamblaje de interoperabilidad primario , (ya sea usando TLBimp o manualmente) para que todas las interfaces se definan en un espacio de nombres común. Tenga en cuenta que este ensamblaje normalmente no contiene ninguna implementación y, por lo tanto, no debería necesitar hacer referencia a otros ensamblajes (a menos que esos otros ensamblajes también sean ensamblajes de interoperabilidad que declaren interfaces COM dependientes).

  2. Cree un contenedor (por ejemplo en Managed C ++) que realice las llamadas a través de COM "tradicional", en lugar de a través de la interoperabilidad COM de .Net.

La opción 1. definitivamente me parece la mejor opción: tenga en cuenta que no es necesario registrar este ensamblaje ni colocarlo en el GAC, y la implementación puede realizarse en un ensamblaje completamente separado siempre que se haga referencia al ensamblado de interoperabilidad común.

No puedo pensar en muchas situaciones legítimas donde una Asamblea de Interoperabilidad Primaria no es factible.


Mi experiencia es que Microsoft lo hizo de esta manera por diseño. No quieren que sus dos conjuntos de códigos administrados se comuniquen a través de COM. Si intenta agregar un conjunto que admita COM como una referencia COM en su proyecto, el error lo dice.

Si las interfaces COM son la única forma en que puede obtener la funcionalidad que necesita, una envoltura no administrada debería hacer el truco. Escriba un nuevo servidor COM en C ++ o VB6 (o cualquier lenguaje no administrado que sea compatible con COM) que envuelva su servidor COM administrado por terceros. Luego, agregue este nuevo contenedor DLL a su proyecto de código administrado como un servidor COM.


Parece que hay dos problemas:

  1. Acceso a las interfaces
  2. Testigo del componente.

En primer lugar, ¿por qué hacer referencia a él como un objeto COM en primer lugar si sabe que está gestionado? ¿Por qué no hacer una referencia directa a la DLL en lugar de hacerlo a través de interoperabilidad y de esa manera, debería tener acceso a las interfaces de la misma manera que la biblioteca tiene acceso a ellas? Por lo tanto, no necesitarías llamar a Activator.CreateInstance . En su lugar, llamaría var foo = new FIPreviewHandler(); . Sin embargo, eso no resuelve el problema de bitness por lo que el punto es discutible. En lugar...

En cuanto al segundo problema, una solución es colocar el componente COM en una aplicación de servidor COM +. Las aplicaciones COM + determinan su bitness cuando se cargan por primera vez. Si todas las bibliotecas en la aplicación COM + son de 32 bits, cuando se cargue, se cargará en un WOW32 y podrá llamarlo desde una aplicación de 64 bits. He usado este truco para las bibliotecas COM que solo existían en formato de 32 bits, pero necesitaba el uso de ellas en un servidor de 64 bits. La desventaja es que está cargando la biblioteca en un proceso separado y está incurriendo en costos de cálculo debido a que la ejecución está fuera de proceso para su aplicación, pero una vez que se crea una instancia, debería tener el rendimiento suficiente.


Si estoy leyendo esto correctamente, debes forzarlo a usar COM. Dos ideas:

  1. ¿Cambia el comportamiento de Activator.CreateInstance() si obtiene el tipo a través de GetTypeFromProgID() lugar de GetTypeByCLSID() ?

  2. ¿Cómo se comporta Microsoft.VisualBasic.Interaction.CreateObject() ? Sí, odio incluir a VB en esto, pero tengo curiosidad si devuelve el objeto COM en lugar de la clase .NET.


Use PInvoke para llamar a la función COM CoCreateInstance (Ex) y pásele el CLSID definido en su ensamblaje de interoperabilidad y el IID de IPreviewHandler. De esta manera .NET no tiene oportunidad de interferir.

Usted dice que tiene una asamblea de interoperabilidad. Es mejor si se trata de un PIA distribuido por el autor de la asamblea de terceros, pero está bien si tiene que crearlo usted mismo. (Los PIA no tienen que estar registrados en el GAC, aunque a menudo sí lo están). Puede obtener el CLSID cargando el ensamblaje de interoperabilidad en el desensamblador IL y buscando el atributo System.Runtime.InteropServices.GuidAttribute en la clase.


Hombre, si yo fuera tú, solo haría la envoltura, solo cuando el tipo NO sea un tipo COM . Para saber si el tipo creado es un tipo COM, use el objeto IsCOMObject del Type del objeto:

myObject.GetType().IsCOMObject

Si esto es FALSO, cree un contenedor, que use la reflexión para llamar al tipo administrado. La reflexión es lenta, pero puede almacenar en caché los objetos MethodInfo que obtiene ... de lo contrario, puede generar un código IL y crear un contenedor que no tendrá ninguna reflexión.

Por supuesto que hay otros métodos, de hacerlo ... esto depende de usted.

Como estoy enamorado de la generación dinámica de IL en tiempo de ejecución, puedo proporcionarle un código que hace esto ... ¡si está interesado!