Compartir a través de


Generar componentes de COM para la interoperabilidad

Si piensa escribir en el futuro aplicaciones basadas en COM, puede diseñar el código de manera que interopere de manera eficiente con código administrado. Con un diseño avanzado, puede simplificar también la migración de código no administrado a código administrado.

En las siguientes recomendaciones se resumen los procedimientos recomendados a la hora de escribir tipos COM que interactúan con código administrado.

Proporcionar bibliotecas de tipos

En la mayoría de las situaciones, Common Language Runtime requiere metadatos para todos los tipos, incluidos los tipos COM. El Importador de la biblioteca de tipos (Tlbimp.exe), incluido en .NET Framework SDK, puede convertir bibliotecas de tipos COM en metadatos de .NET Framework. Una vez convertida la biblioteca de tipos a metadatos, los clientes administrados pueden llamar fácilmente al tipo COM. Para facilitar su uso, proporcione siempre información de tipos en una biblioteca de tipos.

Puede empaquetar una biblioteca de tipos como un archivo independiente o incrustarla como un recurso dentro de un archivo .dll, .exe u .ocx. Además, puede generar metadatos directamente, lo que permite firmar los metadatos con el par de claves de la compañía de software. Los metadatos firmados con una clave tienen un origen conocido y pueden ayudar a impedir el enlace cuando el llamador tiene una clave incorrecta, con lo que se mejora la seguridad.

Registrar bibliotecas de tipos

Para calcular referencias a llamadas correctamente, puede que el motor en tiempo de ejecución tenga que encontrar la biblioteca de tipos donde se describe un tipo concreto. Una biblioteca de tipos debe estar registrada para que el motor en tiempo de ejecución pueda verla, excepto en el caso del enlace en tiempo de ejecución.

Puede registrar una biblioteca de tipos si llama a la función LoadTypeLibEx de la API de Microsoft Win32 con el indicador regkind establecido como REGKIND_REGISTER. Regsvr32.exe registra automáticamente una biblioteca de tipos incrustada dentro de un archivo .dll.

Utilizar matrices seguras en lugar de matrices de longitud variable

Las matrices seguras de COM son autodescriptivas. Al examinar la matriz segura, el contador de referencias en tiempo de ejecución puede determinar el rango, el tamaño, los límites y normalmente el tipo de contenido de la matriz en tiempo de ejecución. Las matrices de longitud variable (o de estilo C) no tienen la misma calidad autodescriptiva. Por ejemplo, en la siguiente firma de método no administrado no se proporciona ninguna información acerca del parámetro de matriz excepto el tipo de elemento.

HRESULT DoSomething(int cb, [in] byte buf[]);

De hecho, no se puede distinguir la matriz de otros parámetros pasados por referencia. Por tanto, Tlbimp.exe no convierte el parámetro de matriz del método DoSomething. En su lugar, la matriz aparece como una referencia a un tipo Byte, como se muestra en el siguiente código.

Public Sub DoSomething(cb As Integer, ByRef buf As Byte)
public void DoSomething(int cb, ref Byte buf);

Para mejorar la interoperación, puede escribir el argumento como un SAFEARRAY en la firma del método no administrado. Por ejemplo:

HRESULT DoSomething(SAFEARRAY(byte)buf);

Tlbimp.exe convierte el SAFEARRAY en el siguiente tipo de matriz administrada:

Public Sub DoSomething(buf As Byte())
public void DoSomething(Byte[] buf);

Utilizar tipos de datos compatibles con Automatización

El servicio de contador de referencias en tiempo de ejecución es compatible automáticamente con todos los tipos de datos compatibles con Automatización. Puede que no se admitan los tipos que no sean compatibles.

Proporcionar la versión y la configuración regional en las bibliotecas de tipos

Cuando importa una biblioteca de tipos, la información de versión y de configuración regional de dicha biblioteca también se propaga al ensamblado. Los clientes administrados se pueden enlazar a una determinada versión o configuración regional del ensamblado o a la versión más reciente del ensamblado. Al proporcionar información de versión en la biblioteca de tipos los clientes pueden elegir con precisión qué versión del ensamblado desean utilizar.

Utilizar tipos que se pueden representar como bits o bytes

Los tipos de datos se pueden o no se pueden representar como bits o bytes. Los tipos que se pueden representar como bits o bytes tienen una representación común en el límite de interoperabilidad. Los tipos entero y de punto flotante se pueden representar como bits o bytes. Las matrices y las estructuras de tipos que se pueden representar como bits o bytes también se pueden representar como bits o bytes. Las cadenas, fechas y objetos son ejemplos de tipos que no se pueden representar como bits o bytes que se convierten durante el proceso de cálculo de referencias.

El servicio de cálculo de referencia de interoperabilidad admite tipos que se pueden y que no se pueden representar como bits y bytes; sin embargo, los tipos que requieren conversión durante el cálculo de referencias no funcionan bien como tipos que se pueden representar como bits o bytes. Cuando utilice tipos que no se pueden representar como bits y bytes, tenga en cuenta que hay una sobrecarga asociada al cálculo de sus referencias.

Las cadenas son especialmente problemáticas. Las cadenas administradas se almacenan como caracteres Unicode y, por tanto, se puede calcular sus referencias de forma mucho más eficiente que en el código no administrado que espera argumentos de caracteres Unicode. Es mejor evitar cadenas formadas por caracteres ANSI siempre que sea posible.

Implementar IProvideClassInfo

Al calcular referencias de interfaces no administradas en código administrado, el motor en tiempo de ejecución crea un contenedor de un tipo específico. La firma de método suele indicar el tipo de interfaz, pero el tipo del objeto que implementa la interfaz puede ser desconocido. Si el tipo de objeto es desconocido, el motor en tiempo de ejecución contiene la interfaz con un contenedor de objetos COM genérico, que es menos funcional que los contenedores específicos de tipos.

Por ejemplo, considere la siguiente firma de método COM:

interface INeedSomethng {
   HRESULT DoSomething(IBiz *pibiz);
}

Cuando se importa, el método se convierte de la siguiente manera:

Interface INeedSomething
   Sub DoSomething(pibiz As IBiz)
End Interface
interface INeedSomething {
   void DoSomething(IBiz pibiz);
}

Si pasa un objeto administrado que implementa la interfaz INeedSomething a la interfaz IBiz, el contador de referencias de interoperabilidad intentará contener la interfaz con un contenedor de objetos de un tipo específico en la inclusión inicial de IBiz en el código administrado. Para identificar el tipo correcto de contenedor, el contador de referencias debe saber el tipo del objeto que implementa la interfaz. Una de las formas en que el contador de referencias intenta determinar el tipo de objeto es consultar la interfaz IProvideClassInfo. Si el objeto implementa IProvideClassInfo, el contador de referencias determina el tipo de objeto y contiene la interfaz en un contenedor con información de tipos.

Utilizar llamadas modulares

El cálculo de referencias de datos entre código administrado y no administrado tiene un costo. Puede mitigar ese costo si hace menos transiciones en el límite. Las interfaces que reducen al mínimo el número de transiciones suelen funcionar mejor que las interfaces que cruzan el límite con frecuencia, realizando pequeñas tareas cada vez que lo cruzan.

Utilizar valores HRESULT de error de manera conservadora

Cuando un cliente administrado llama a un objeto COM, el motor en tiempo de ejecución asigna los valores HRESULT de error del objeto COM a excepciones, que el contador de referencias produce al volver de la llamada. El modelo de excepciones administradas se ha optimizado para los casos que no sean excepcionales; no hay prácticamente ninguna sobrecarga asociada a la detección de excepciones cuando no se produce ninguna excepción. Por el contrario, cuando se produce una excepción, la detección de la excepción puede ser muy costosa.

Utilice las excepciones con moderación y evite devolver valores HRESULT de error con fines informativos. Reserve los valores HRESULT de error para situaciones excepcionales. Tenga en cuenta que un uso excesivo de los valores HRESULT de error puede afectar al rendimiento.

Liberar recursos externos explícitamente

Algunos objetos utilizan recursos externos durante su vida útil; por ejemplo, una conexión de base de datos puede actualizar un conjunto de registros. Normalmente, un objeto consume un recurso externo durante su vida útil, mientras que una liberación explícita puede devolver el recurso inmediatamente. Por ejemplo, puede utilizar el método Close en un objeto de archivo en lugar de cerrar el archivo en el destructor de clase o con IUnknown.Release. Proporcionando un equivalente al método Close en su código, puede liberar el recurso de archivo externo aunque el objeto de archivo siga existiendo.

Evitar la redefinición de tipos no administrados

La forma correcta de implementar una interfaz COM existente en el código administrado consiste en empezar por importar la definición de la interfaz con Tlbimp.exe o una API equivalente. Los metadatos resultantes proporcionan una definición compatible de la interfaz COM (el mismo IID, los mismos DispId, etc.).

Evite redefinir manualmente las interfaces COM en el código administrado. Esta tarea consume mucho tiempo y raras veces genera una interfaz administrada compatible con la interfaz COM existente. En su lugar, utilice Tlbimp.exe para mantener la compatibilidad de las definiciones.

Evitar el uso de valores HRESULT correctos

La detección de excepciones es la forma más natural en que las aplicaciones administradas tratan las situaciones de error. Para hacer que el uso de tipos COM sea transparente, el motor en tiempo de ejecución produce automáticamente una excepción siempre que un método COM devuelve un valor HRESULT de error.

Si su objeto COM devuelve un valor HRESULT correcto, el motor en tiempo de ejecución devolverá el valor existente en el parámetro retval. De manera predeterminada se descarta el HRESULT, lo que hace muy difícil que el cliente administrado examine el valor de un HRESULT correcto. Aunque puede conservar un valor HRESULT con el atributo PreserveSigAttribute, el proceso requiere bastante esfuerzo. Debe agregar manualmente el atributo a un ensamblado generado con Tlbimp.exe o una API equivalente.

Es mejor evitar el uso de valores HRESULT correctos siempre que sea posible. En su lugar, puede devolver información acerca del estado de una llamada mediante un parámetro Out.

Evitar el uso de funciones de módulo

Las bibliotecas de tipos pueden contener funciones definidas en un módulo. Normalmente se utilizan estas funciones para proporcionar información de tipos a los puntos de entrada de archivo DLL. Tlbimp.exe no importa estas funciones.

Evitar el uso de miembros de System.Object en interfaces predeterminadas

Los clientes administrados y las coclases COM interactúan entre sí con ayuda de los contenedores proporcionados por el motor en tiempo de ejecución. Cuando importa un tipo COM, el proceso de conversión agrega todos los métodos de la interfaz predeterminada de la coclase a la clase del contenedor, que se deriva de la clase System.Object. Tenga cuidado al asignar nombre a los miembros de la interfaz predeterminada, de manera que no haya conflictos de nombres con los miembros de System.Object. Si se produce un conflicto, el método importado reemplaza al método de clase base.

Esta acción puede ser adecuada si el método de la interfaz predeterminada y el método de System.Object proporcionan la misma funcionalidad. Sin embargo, puede ser problemática si se utilizan métodos de la interfaz predeterminada de una forma no deseada. Para evitar conflictos de nombres, evite el uso de los siguientes nombres en las interfaces predeterminadas: Object, Equals, Finalize, GetHashCode, GetType, MemberwiseClone y ToString.

Vea también

Referencia

Importador de la biblioteca de tipos (TlbImp.exe)

Otros recursos

Consideraciones de diseño para interoperaciones