Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
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, incorporado e exclusivo para Windows (não gerado pelo código-fonte) do .NET, gera um stub de IL—um fluxo de instruções IL que é compilado pelo JIT—em tempo de execução para facilitar a transição do código gerenciado para COM e vice-versa. Como este stub IL é gerado em tempo de execução, é incompatível com o NativeAOT e o IL trimming. A geração de stubs em tempo de execução também pode dificultar o diagnóstico de problemas de marshalling.
A interoperabilidade incorporada utiliza 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 otimizada para redução de tamanho e compatível com AOT.
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 visibilidade internal ou public para o código gerado 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 internal ou public.
[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 pode-se usar o tipo StrategyBasedComWrappers ou um tipo derivado personalizado para consumir ou para 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 atributo MarshalUsingAttribute e alguns usos do atributo MarshalAsAttribute para personalizar o marshaling de parâmetros. Para obter mais informações, consulte como personalizar o marshalling gerado pela fonte com o atributo MarshalUsing e personalizar o marshalling de parâmetros com o atributo MarshalAs. As propriedades GeneratedComInterfaceAttribute.StringMarshalling e GeneratedComInterfaceAttribute.StringMarshallingCustomType aplicam-se a todos os parâmetros e tipos de retorno do tipo string na interface, se não tiverem outros atributos de marshalling.
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 HRESULT, que é um tipo inteiro de 4 bytes e representa estados de 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 tu quiseres lidar tu próprio com o HRESULT, podes usar o PreserveSigAttribute no método para indicar ao gerador que 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 no .NET interop.
Incompatibilidades e diferenças do COM incorporado
IUnknown apenas
A única base de interface suportada é IUnknown. Interfaces com um InterfaceTypeAttribute que tem um valor diferente de InterfaceIsIUnknown não é suportada no COM gerado por 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 a partir do código-fonte tem alguns comportamentos padrão de marshaling 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 o comportamento de[Out]ou[In, Out]for necessário em outros tipos, utilize os modificadores de parâmetroineout.
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 tem suporte definir uma interface com o atributo GeneratedComInterfaceAttribute que deriva de uma interface com atributo GeneratedComInterface 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 offsets de métodos virtuais gerados na cadeia de interfaces base definida em outro assembly não serão consideradas 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 este aviso para reconhecer as limitações e herdar através dos limites de montagem.
Marshal APIs
Algumas APIs em Marshal não são compatíveis com o COM gerado a partir do código-fonte. Substitua esses métodos por seus métodos correspondentes em uma ComWrappers implementação.