Partilhar 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")]
static nint GetPointerToComInterface(); // C definition - IUnknown* GetPointerToComInterface();

[DllImport("MyComObjectProvider")]
static void GivePointerToComInterface(nint comObject); // C definition - void GivePointerToComInterface(IUnknown* pUnk);

// 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 internal publicou .

[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")]
private static partial nint GetPointerToComInterface(); // C definition - IUnknown* GetPointerToComInterface();

[LibraryImport("MyComObjectProvider")]
private static partial void GivePointerToComInterface(nint comObject); // C definition - void GivePointerToComInterface(IUnknown* pUnk);

// 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.GetOrCreateObjectForComInstance(ptr, CreateObjectFlags.None);
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, CreateComInterfaceFlags.None);
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.

Interfaces derivadas através dos limites de montagem

No .NET 8, não há suporte para definir uma interface com o atributo que deriva GeneratedComInterfaceAttribute de uma GeneratedComInterfaceinterface -atribuída definida em outro assembly.

No .NET 9 e versões posteriores, esse cenário é suportado com as seguintes restrições:

  • O tipo de interface base deve ser compilado visando a mesma estrutura de destino que o tipo derivado.
  • O tipo de interface base não deve sombrear nenhum membro de sua interface base, se tiver um.

Além disso, quaisquer alterações em qualquer compensação de método virtual gerado na cadeia de interface base definida em outro assembly não serão contabilizadas nas interfaces derivadas até que o projeto seja reconstruído.

Nota

No .NET 9 e versões posteriores, um aviso é emitido ao herdar interfaces COM geradas através dos limites do assembly para informá-lo sobre as restrições e armadilhas do uso desse recurso. Você pode desativar esse aviso para reconhecer as limitações e herdar através dos limites do assembly.

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