Share via


Geração de código-fonte para ComWrappers

O .NET 8 apresenta um gerador de código-fonte que cria uma implementação da API ComWrappers para você. O gerador reconhece o GeneratedComInterfaceAttribute.

O sistema de interoperabilidade COM interno (não gerado pelo código-fonte), somente Windows, do tempo de execução do .NET gera um stub de IL — um fluxo de instruções de IL que é JIT — em tempo de execução para facilitar a transição do código gerenciado para COM e vice-versa. Como esse stub IL é gerado em tempo de execução, ele é incompatível com o corte NativeAOT e IL. A geração de stub em tempo de execução também pode dificultar o diagnóstico de problemas de empacotamento.

A interoperabilidade interna usa atributos como ComImport ou DllImport, que dependem da geração de código em tempo de execução. O código a seguir mostra um exemplo disso:

[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);

A ComWrappers API permite interagir com COM em C# sem usar o sistema COM integrado, mas requer clichês substanciais e código inseguro escrito à mão. O gerador de interface COM automatiza este processo e torna ComWrappers tão fácil como o COM incorporado, mas fornece-o de uma forma trimmable e AOT-friendly.

Utilização básica

Para usar o gerador de interface COM, adicione os GeneratedComInterfaceAttribute atributos e GuidAttribute na definição de interface da qual você deseja importar ou expor para COM. O tipo deve ser marcado partial e ter internal visibilidade para public o código gerado para poder acessá-lo.

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

Em seguida, para expor uma classe que implementa uma interface para COM, adicione o GeneratedComClassAttribute à classe de implementação. Esta classe também deve ser partial e ou internalpublicou .

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

Em tempo de compilação, o gerador cria uma implementação da API ComWrappers e você pode usar o StrategyBasedComWrappers tipo ou um tipo derivado personalizado para consumir ou expor a interface 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);

Personalizar o empacotamento

O gerador de interface COM respeita o MarshalUsingAttribute atributo e alguns usos do atributo para personalizar o MarshalAsAttribute empacotamento de parâmetros. Para obter mais informações, consulte como personalizar o empacotamento gerado na fonte com o MarshalUsing atributo e personalizar o marshalling de parâmetros com o MarshalAs atributo. As GeneratedComInterfaceAttribute.StringMarshalling propriedades e GeneratedComInterfaceAttribute.StringMarshallingCustomType se aplicam a todos os parâmetros e tipos de tipo string de retorno na interface se eles não tiverem outros atributos de empacotamento.

HRESULTs implícitos e PreserveSig

Os métodos COM em C# têm uma assinatura diferente dos métodos nativos. O COM padrão tem um tipo de retorno de , um tipo inteiro de 4 bytes que representa estados de HRESULTerro e sucesso. Esse HRESULT valor de retorno é oculto por padrão na assinatura C# e convertido em uma exceção quando um valor de erro é retornado. O último parâmetro "out" da assinatura COM nativa pode, opcionalmente, ser convertido no retorno na assinatura C#.

Por exemplo, os trechos a seguir mostram assinaturas do método C# e a assinatura nativa correspondente que o gerador infere.

void Method1(int i);

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

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

Se você quiser lidar com o HRESULT mesmo, você pode usar o PreserveSigAttribute método on para indicar que o gerador não deve fazer essa transformação. Os trechos a seguir demonstram qual assinatura nativa o gerador espera quando [PreserveSig] é aplicado. Os métodos COM devem retornar HRESULT, portanto, o valor de retorno de qualquer método com PreserveSig deve 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 obter mais informações, consulte Traduções de assinatura de método implícito na interoperabilidade .NET

Incompatibilidades e diferenças para COM integrado

IUnknown apenas

A única base de interface suportada é IUnknown. Interfaces com um InterfaceTypeAttribute que tem um valor diferente do InterfaceIsIUnknown que não são suportados no COM gerado pelo código-fonte. Presume-se que quaisquer interfaces sem um InterfaceTypeAttribute derivam de IUnknown. Isso difere do COM interno, onde o padrão é InterfaceIsDual.

Marshalling defaults e suporte

O COM gerado pelo código-fonte tem alguns comportamentos de empacotamento padrão diferentes do COM interno.

  • No sistema COM integrado, todos os tipos têm um atributo implícito [In] , exceto matrizes de elementos blittable, que têm atributos implícitos [In, Out] . No COM gerado pela fonte, todos os tipos, incluindo matrizes de elementos blittable, têm [In] semântica.

  • [In] e [Out] atributos só são permitidos em matrizes. Se [Out] o comportamento for [In, Out] necessário em outros tipos, use os modificadores de in parâmetro e out .

Interfaces derivadas

No sistema COM interno, se você tiver interfaces que derivam de outras interfaces COM, deverá declarar um método de sombreamento para cada método base nas interfaces base com a new palavra-chave. Para obter mais informações, consulte Herança da interface COM e .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);
}

O gerador de interface COM não espera qualquer sombreamento de métodos base. Para criar um método que herda de outro, basta indicar a interface base como uma interface base C# e adicionar os métodos da interface derivada. Para obter mais informações, consulte o documento de design.

[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);
}

Observe que uma interface com o GeneratedComInterface atributo só pode herdar de uma interface base que tenha o GeneratedComInterface atributo.

Marshal APIs

Algumas APIs não são compatíveis com o COM gerado pelo Marshal código-fonte. Substitua esses métodos por seus métodos correspondentes em uma ComWrappers implementação.

Consulte também