Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Exponer tipos de .NET a COM
Si piensa exponer tipos en un ensamblado a aplicaciones COM, tenga en cuenta los requisitos de interoperabilidad COM en tiempo de diseño. Los tipos administrados (clase, interfaz, estructura y enumeración) se integran perfectamente con los tipos COM cuando se cumplen las instrucciones siguientes:
Las clases deben implementar interfaces explícitamente.
Aunque la interoperabilidad COM proporciona un mecanismo para generar automáticamente una interfaz que contiene todos los miembros de la clase y los miembros de su clase base, es mucho mejor proporcionar interfaces explícitas. La interfaz generada automáticamente se denomina interfaz de clase. Para obtener instrucciones, consulte Introducción a la interfaz de clase.
Puede usar Visual Basic, C#y C++ para incorporar definiciones de interfaz en el código, en lugar de tener que usar el lenguaje de definición de interfaz (IDL) o su equivalente. Para más información sobre la sintaxis, consulte la documentación del lenguaje.
Los tipos administrados deben ser públicos.
Solo los tipos públicos de un ensamblado se registran y exportan a la biblioteca de tipos. Como resultado, solo los tipos públicos son visibles para COM.
Los tipos administrados exponen características a otro código administrado que podría no exponerse a COM. Por ejemplo, los constructores con parámetros, los métodos estáticos y los campos constantes no se exponen a los clientes COM. Además, a medida que el tiempo de ejecución maneja los datos al entrar y salir de un tipo, los datos se pueden copiar o transformar.
Los métodos, propiedades, campos y eventos deben ser públicos.
Los miembros de tipos públicos también deben ser públicos para que sean visibles para COM. Puede restringir la visibilidad de un ensamblado, un tipo público o miembros públicos de un tipo público aplicando el ComVisibleAttribute. De forma predeterminada, todos los tipos públicos y miembros están visibles.
Los tipos deben tener un constructor público sin parámetros para activarse desde COM.
Los tipos públicos administrados son visibles para COM. Sin embargo, sin un constructor público sin parámetros (un constructor sin argumentos), los clientes COM no pueden crear el tipo. Los clientes COM todavía pueden usar el tipo si se activa por algún otro medio.
Los tipos no pueden ser abstractos.
Ni los clientes COM ni los clientes .NET pueden crear tipos abstractos.
Cuando se exporta a COM, la jerarquía de herencia de un tipo administrado se aplana. El control de versiones también difiere entre entornos administrados y no administrados. Los tipos expuestos a COM no tienen las mismas características de control de versiones que otros tipos administrados.
Consumo de tipos COM desde .NET
Si piensa consumir tipos COM de .NET y no quiere usar herramientas como Tlbimp.exe (Importador de biblioteca de tipos), debe seguir estas instrucciones:
- Las interfaces deben tener ComImportAttribute aplicado.
- Las interfaces deben tener aplicado GuidAttribute con el identificador de interfaz para la interfaz COM.
- Las interfaces deben tener InterfaceTypeAttribute aplicado para especificar el tipo de interfaz base de esta interfaz (
IUnknown
,IDispatch
oIInspectable
).- La opción predeterminada es tener el tipo base de
IDispatch
y anexar los métodos declarados a la tabla de funciones virtuales esperada para la interfaz. - Solo .NET Framework admite la especificación de un tipo base de
IInspectable
.
- La opción predeterminada es tener el tipo base de
Estas directrices proporcionan los requisitos mínimos para escenarios comunes. Existen muchas más opciones de personalización y se describen en Aplicar atributos de interoperabilidad.
Definición de interfaces COM en .NET
Cuando el código .NET intenta llamar a un método en un objeto COM a través de una interfaz con el ComImportAttribute atributo , debe crear una tabla de funciones virtuales (también conocida como vtable o vftable) para formar la definición de .NET de la interfaz para determinar el código nativo al que se debe llamar. Este proceso es complejo. En los ejemplos siguientes se muestran algunos casos sencillos.
Considere una interfaz COM con algunos métodos:
struct IComInterface : public IUnknown
{
STDMETHOD(Method)() = 0;
STDMETHOD(Method2)() = 0;
};
Para esta interfaz, en la tabla siguiente se describe su diseño de tabla de funciones virtuales:
IComInterface ranura de tabla de funciones virtuales |
Nombre del método |
---|---|
0 | IUnknown::QueryInterface |
1 | IUnknown::AddRef |
2 | IUnknown::Release |
3 | IComInterface::Method |
4 | IComInterface::Method2 |
Cada método se agrega a la tabla de funciones virtuales en el orden en que se declaró. El orden concreto se define mediante el compilador de C++, pero para casos simples sin sobrecargas, el orden de declaración define el orden en la tabla.
Declare una interfaz de .NET que corresponda a esta interfaz de la siguiente manera:
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid(/* The IID for IComInterface */)]
interface IComInterface
{
void Method();
void Method2();
}
InterfaceTypeAttribute especifica la interfaz base. Proporciona algunas opciones:
Valor de ComInterfaceType | Tipo de interfaz base | Comportamiento de los miembros en la interfaz con atributos |
---|---|---|
InterfaceIsIUnknown |
IUnknown |
En primer lugar, la tabla de funciones virtuales contiene los miembros de IUnknown , y a continuación, los miembros de esta interfaz en orden de declaración. |
InterfaceIsIDispatch |
IDispatch |
Los miembros no se agregan a la tabla de funciones virtuales. Solo son accesibles a través de IDispatch . |
InterfaceIsDual |
IDispatch |
En primer lugar, la tabla de funciones virtuales contiene los miembros de IDispatch , y a continuación, los miembros de esta interfaz en orden de declaración. |
InterfaceIsIInspectable |
IInspectable |
En primer lugar, la tabla de funciones virtuales contiene los miembros de IInspectable , y a continuación, los miembros de esta interfaz en orden de declaración. Solo se admite en .NET Framework. |
Herencia de interfaz COM y .NET
El sistema de interoperabilidad COM que usa el ComImportAttribute no interactúa con la herencia de interfaz, por lo que puede provocar un comportamiento inesperado a menos que se tomen algunas medidas de mitigación.
El generador de origen COM que utiliza el atributo System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute
interactúa con la herencia de interfaz, por lo que se comporta más como se espera.
Herencia de interfaz COM en C++
En C++, los desarrolladores pueden declarar interfaces COM que derivan de otras interfaces COM de la siguiente manera:
struct IComInterface : public IUnknown
{
STDMETHOD(Method)() = 0;
STDMETHOD(Method2)() = 0;
};
struct IComInterface2 : public IComInterface
{
STDMETHOD(Method3)() = 0;
};
Este estilo de declaración se usa regularmente como mecanismo para agregar métodos a objetos COM sin cambiar las interfaces existentes, lo que sería un cambio importante. Este mecanismo de herencia da como resultado los siguientes diseños de tabla de funciones virtuales:
IComInterface ranura de tabla de funciones virtuales |
Nombre del método |
---|---|
0 | IUnknown::QueryInterface |
1 | IUnknown::AddRef |
2 | IUnknown::Release |
3 | IComInterface::Method |
4 | IComInterface::Method2 |
IComInterface2 ranura de tabla de funciones virtuales |
Nombre del método |
---|---|
0 | IUnknown::QueryInterface |
1 | IUnknown::AddRef |
2 | IUnknown::Release |
3 | IComInterface::Method |
4 | IComInterface::Method2 |
5 | IComInterface2::Method3 |
Como resultado, es fácil llamar a un método definido en IComInterface
desde un IComInterface2*
. En concreto, llamar a un método en una interfaz base no requiere una llamada para QueryInterface
obtener un puntero a la interfaz base. Además, C++ permite una conversión implícita de IComInterface2*
a IComInterface*
, que está bien definida y le permite evitar llamar a QueryInterface
nuevamente. Como resultado, en C o C++, nunca tendrá que llamar QueryInterface
para llegar al tipo base si no quiere, lo que puede permitir algunas mejoras de rendimiento.
Nota:
Las interfaces de WinRT no siguen este modelo de herencia. Se definen para seguir el mismo modelo que el modelo de interoperabilidad COM basado en [ComImport]
de .NET.
Herencia de interfaz con ComImportAttribute
En .NET, el código de C# que parece la herencia de interfaz no es realmente herencia de interfaz. Observe el código siguiente:
interface I
{
void Method1();
}
interface J : I
{
void Method2();
}
Este código no dice, "J
implementa I
". En realidad, el código dice: "cualquier tipo que implemente J
también debe implementar I
". Esta diferencia conduce a la decisión fundamental de diseño que hace que la herencia de interfaz en la interoperabilidad basada en ComImportAttribute sea poco ergonómica. Las interfaces siempre se consideran por sí solas; la lista de interfaces base de una interfaz no tiene ningún impacto en los cálculos para determinar una tabla de funciones virtuales para una interfaz .NET determinada.
Como resultado, el equivalente natural del ejemplo anterior de la interfaz COM de C++ conduce a un diseño de tabla de función virtual diferente.
Código de C#:
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface
{
void Method();
void Method2();
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface2 : IComInterface
{
void Method3();
}
Diseños de tabla de funciones virtuales:
IComInterface ranura de tabla de funciones virtuales |
Nombre del método |
---|---|
0 | IUnknown::QueryInterface |
1 | IUnknown::AddRef |
2 | IUnknown::Release |
3 | IComInterface::Method |
4 | IComInterface::Method2 |
IComInterface2 ranura de tabla de funciones virtuales |
Nombre del método |
---|---|
0 | IUnknown::QueryInterface |
1 | IUnknown::AddRef |
2 | IUnknown::Release |
3 | IComInterface2::Method3 |
Dado que estas tablas de funciones virtuales difieren del ejemplo de C++, esto provocará problemas graves en tiempo de ejecución. La definición correcta de estas interfaces en .NET con ComImportAttribute es la siguiente:
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface
{
void Method();
void Method2();
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface2 : IComInterface
{
new void Method();
new void Method2();
void Method3();
}
En el nivel de metadatos, IComInterface2
no implementa IComInterface
, sino que solo especifica que los implementadores de IComInterface2
también deben implementar IComInterface
. Por lo tanto, cada método de los tipos de interfaz base debe volver a declararse.
Herencia de interfaz con GeneratedComInterfaceAttribute
(.NET 8 y versiones posteriores)
El generador de código fuente COM, desencadenado por GeneratedComInterfaceAttribute
, implementa la herencia de la interfaz de C# como herencia de interfaz COM, por lo que las tablas de funciones virtuales se disponen según lo previsto. Si toma el ejemplo anterior, la definición correcta de estas interfaces en .NET con System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute
es la siguiente:
[GeneratedComInterface]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface
{
void Method();
void Method2();
}
[GeneratedComInterface]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface2 : IComInterface
{
void Method3();
}
No es necesario ni se debe volver a declarar los métodos de las interfaces base. En la tabla siguiente se describen las tablas de funciones virtuales resultantes:
IComInterface ranura de tabla de funciones virtuales |
Nombre del método |
---|---|
0 | IUnknown::QueryInterface |
1 | IUnknown::AddRef |
2 | IUnknown::Release |
3 | IComInterface::Method |
4 | IComInterface::Method2 |
IComInterface2 ranura de tabla de funciones virtuales |
Nombre del método |
---|---|
0 | IUnknown::QueryInterface |
1 | IUnknown::AddRef |
2 | IUnknown::Release |
3 | IComInterface::Method |
4 | IComInterface::Method2 |
5 | IComInterface2::Method3 |
Como puede ver, estas tablas coinciden con el ejemplo de C++, por lo que estas interfaces funcionarán correctamente.