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
public
ou .
[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 HRESULT
erro 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 dein
parâmetro eout
.
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 GeneratedComInterface
interface -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.