Compartir a través de


Generación de origen para ComWrappers

.NET 8 presenta un generador de origen que crea una implementación de API de ComWrappers automáticamente. El generador reconoce GeneratedComInterfaceAttribute.

El sistema de interoperabilidad COM integrado en el tiempo de ejecución de .NET (no generado en origen) solo para Windows genera un código auxiliar (un flujo de instrucciones de IL que se entrega cuando es necesario) en tiempo de ejecución para facilitar la transición del código administrado a COM y viceversa. Dado que este código auxiliar de IL se genera en tiempo de ejecución, no es compatible con el recorte nativeAOT y IL. La generación de código auxiliar en tiempo de ejecución también puede dificultar el diagnóstico de problemas de serialización.

La interoperabilidad integrada usa atributos como ComImport o DllImport, que dependen de la generación de código en tiempo de ejecución. El código siguiente muestra un ejemplo de esto:

[ComImport]
interface IFoo
{
    void Method(int i);
}

[DllImport("MyComObjectProvider.dll")]
static nint GetPointerToComInterface();

[DllImport("MyComObjectProvider.dll")]
static void GivePointerToComInterface(nint comObject);

// Use the system to create a Runtime Callable Wrapper to use in managed code
nint ptr = GetPointerToComInterface();
IFoo foo = (IFoo)Marshal.GetObjectForIUnknown(ptr);
foo.Method(0);
...
// Use the system to create a COM Callable Wrapper to pass to unmanaged code
IFoo foo = GetManagedIFoo();
nint ptr = Marshal.GetIUnknownForObject(foo);
GivePointerToComInterface(ptr);

La API ComWrappers permite interactuar con COM en C# sin usar el sistema COM integrado, pero requiere escribir a mano una gran cantidad de código no seguro y genérico. El generador de interfaz COM automatiza este proceso y hace que ComWrappers sea tan sencillo como el COM integrado, pero lo entrega de forma recortable y muy compatible con AOT.

Uso básico

Para usar el generador de interfaz COM, agregue los atributos GeneratedComInterfaceAttribute y GuidAttribute en la definición de interfaz desde la que desea importar o exponer a COM. El tipo debe marcarse como partial y tener visibilidad internal o public para que el código generado pueda acceder a él.

[GeneratedComInterface]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
internal partial interface IFoo
{
    void Method(int i);
}

A continuación, para exponer una clase que implementa una interfaz en COM, agregue GeneratedComClassAttribute a la clase de implementación. Esta clase también debe ser partial y internal o public.

[GeneratedComClass]
internal partial class Foo : IFoo
{
    public void Method(int i)
    {
        // Do things
    }
}

En tiempo de compilación, el generador crea una implementación de la API de ComWrappers y puede usar el tipo StrategyBasedComWrappers o un tipo derivado personalizado para consumir o exponer la interfaz COM.

[LibraryImport("MyComObjectProvider.dll")]
static nint GetPointerToComInterface();

[LibraryImport("MyComObjectProvider.dll")]
static void GivePointerToComInterface(nint comObject);

// Use the ComWrappers API to create a Runtime Callable Wrapper to use in managed code
ComWrappers cw = new StrategyBasedComWrappers();
nint ptr = GetPointerToComInterface();
IFoo foo = (IFoo)cw.GetOrCreateObjectForComInterface(ptr);
foo.Method(0);
...
// Use the system to create a COM Callable Wrapper to pass to unmanaged code
ComWrappers cw = new StrategyBasedComWrappers();
Foo foo = new();
nint ptr = cw.GetOrCreateComInterfaceForObject(foo);
GivePointerToComInterface(ptr);

Personalización de la serialización

El generador de interfaz COM respeta el atributo MarshalUsingAttribute y algunos usos del atributo MarshalAsAttribute para personalizar la serialización de parámetros. Para obtener más información, consulte cómo personalizar la serialización generada por el origen con el atributo MarshalUsing y personalizar la serialización de parámetros con el atributo MarshalAs . Las propiedades GeneratedComInterfaceAttribute.StringMarshalling y GeneratedComInterfaceAttribute.StringMarshallingCustomType se aplican a todos los parámetros y tipos devueltos de tipo string en la interfaz si no tienen otros atributos de serialización.

PreserveSig y HRESUL implícitos

Los métodos COM de C# tienen una firma diferente a los métodos nativos. COM estándar tiene un tipo de valor devuelto de HRESULT, un tipo entero de 4 bytes que representa los estados de error y correcto. Este valor devuelto HRESULT está oculto de forma predeterminada en la firma de C# y se convierte en una excepción cuando se devuelve un valor de error. El último parámetro "out" de la firma COM nativa puede convertirse opcionalmente en la devolución en la firma de C#.

Por ejemplo, los fragmentos de código siguientes muestran firmas de método de C# y la firma nativa correspondiente que el generador deduce.

void Method1(int i);

int Method2(float i);
HRESULT Method1(int i);

HRESULT Method2(float i, _Out_ int* returnValue);

Si desea controlar HRESULT usted, puede usar PreserveSigAttribute en el método para indicar que el generador no debe realizar esta transformación. Los fragmentos de código siguientes muestran qué firma nativa espera el generador cuando se aplica [PreserveSig]. Los métodos COM deben devolver HRESULT, por lo que el valor devuelto de cualquier método con PreserveSig debe ser int.

[PreserveSig]
int Method1(int i, out int j);

[PreserveSig]
int Method2(float i);
HRESULT Method1(int i, int* j);

HRESULT Method2(float i);

Para obtener más información, consulte Traducciones implícitas de firmas de método en interoperabilidad de .NET.

Incompatibilidades y diferencias con COM integrada

Solo IUnknown

La única base de interfaz admitida es IUnknown. Las interfaces con un InterfaceTypeAttribute que tiene un valor distinto de InterfaceIsIUnknown no se admiten en COM generado por el origen. Se supone que cualquier interfaz sin un elemento InterfaceTypeAttribute se deriva de IUnknown. Esto difiere de COM integrado donde el valor predeterminado es InterfaceIsDual.

Serialización de valores predeterminados y compatibilidad

El COM generado por el origen tiene algunos comportamientos de serialización predeterminados diferentes de COM integrados.

  • En el sistema COM integrado, todos los tipos tienen un atributo implícito [In], excepto las matrices de elementos que pueden transferirse en bloque de bits, que tienen atributos implícitos [In, Out]. En COM generado por el origen, todos los tipos, incluidas las matrices de elementos que pueden transferirse en bloque de bits, tienen semánticas [In].

  • Los atributos [In] y [Out] solo se permiten en matrices. Si se requiere el comportamiento [Out] o [In, Out] en otros tipos, use los modificadores de parámetro in y out.

Interfaces derivadas

En el sistema COM integrado, si tiene interfaces que derivan de otras interfaces COM, debe declarar un método de "shadowing" para cada método base en las interfaces base con la palabra clave new. Para obtener más información, vea herencia de interfaz de COM y .NET.

[ComImport]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
interface IBase
{
    void Method1(int i);
    void Method2(float i);
}

[ComImport]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
interface IDerived : IBase
{
    new void Method1(int i);
    new void Method2(float f);
    void Method3(long l);
    void Method4(double d);
}

El generador de interfaz COM no espera "shadowing" de métodos base. Para crear un método que herede de otro, simplemente indique la interfaz base como una interfaz base de C# y agregue los métodos de la interfaz derivada. Para más información, consulte la documentación de diseño.

[GeneratedComInterface]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
interface IBase
{
    void Method1(int i);
    void Method2(float i);
}

[GeneratedComInterface]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
interface IDerived : IBase
{
    void Method3(long l);
    void Method4(double d);
}

Tenga en cuenta que una interfaz con el atributo GeneratedComInterface solo puede heredar de una interfaz base que tenga el atributo GeneratedComInterface.

API de serialización

Algunas API de Marshal no son compatibles con COM generado por el origen. Reemplace estos métodos por sus métodos correspondientes en una implementación ComWrappers.

Consulte también