Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Expor tipos .NET ao COM
Se você pretende expor tipos em um assembly a aplicativos COM, considere os requisitos de interoperabilidade com COM no momento do design. Os tipos gerenciados (classe, interface, estrutura e enumeração) integram-se perfeitamente aos tipos COM quando você segue as seguintes diretrizes:
As classes devem implementar interfaces explicitamente.
Embora a interoperabilidade COM forneça um mecanismo para gerar automaticamente uma interface contendo todos os membros da classe e os membros de sua classe base, é muito melhor fornecer interfaces explícitas. A interface gerada automaticamente é chamada de interface de classe. Para obter diretrizes, consulte Introdução à interface de classe.
Você pode usar o Visual Basic, C#e C++ para incorporar definições de interface em seu código, em vez de ter que usar a IDL (Interface Definition Language) ou seu equivalente. Para obter detalhes da sintaxe, consulte a documentação do idioma.
Os tipos gerenciados devem ser públicos.
Somente os tipos públicos em um assembly são registrados e exportados para a biblioteca de tipos. Como resultado, somente tipos públicos são visíveis para COM.
Os tipos gerenciados expõem recursos a outros códigos gerenciados que podem não ser expostos ao COM. Por exemplo, construtores parametrizados, métodos estáticos e campos constantes não são expostos a clientes COM. Além disso, à medida que o tempo de execução realiza o intercâmbio de dados em um tipo e para fora dele, os dados podem ser copiados ou transformados.
Métodos, propriedades, campos e eventos devem ser públicos.
Os membros de tipos públicos também devem ser públicos se desejarem estar visíveis ao COM. Você pode restringir a visibilidade de um assembly, um tipo público ou membros públicos de um tipo público aplicando o ComVisibleAttribute. Com a configuração padrão, todos os tipos e membros públicos são visíveis.
Os tipos devem ter um construtor público sem parâmetros para ser ativado a partir do COM.
Tipos públicos gerenciados são visíveis para COM. No entanto, sem um construtor público sem parâmetros (um construtor sem argumentos), os clientes COM não podem criar o tipo. Os clientes COM ainda poderão usar o tipo se ele for ativado por outros meios.
Os tipos não podem ser abstratos.
Nem clientes COM nem clientes .NET podem criar tipos abstratos.
Quando um tipo gerenciado é exportado para COM, sua hierarquia de herança é nivelada. O controle de versão também difere entre ambientes gerenciados e não gerenciados. Os tipos expostos ao COM não têm as mesmas características de controle de versão que outros tipos gerenciados.
Consumir tipos COM do .NET
Se você pretende consumir tipos COM do .NET e não deseja usar ferramentas como Tlbimp.exe (Importador de Biblioteca de Tipos), siga estas diretrizes:
- As interfaces devem ter a ComImportAttribute aplicada.
- As interfaces devem ter o GuidAttribute aplicado com a ID da interface para a interface COM.
- As interfaces devem ter o InterfaceTypeAttribute aplicado para especificar o tipo de interface base dessa interface (
IUnknownouIDispatchIInspectable).- A opção padrão é ter o tipo base de
IDispatche acrescentar os métodos declarados à tabela de funções virtuais esperada para a interface. - Somente o .NET Framework dá suporte à especificação de um tipo base de
IInspectable.
- A opção padrão é ter o tipo base de
Essas diretrizes fornecem os requisitos mínimos para cenários comuns. Existem muitas outras opções de personalização e são descritas na aplicação de atributos de interoperabilidade.
Definir interfaces COM no .NET
Quando o código .NET tenta chamar um método em um objeto COM por meio de uma interface com o ComImportAttribute atributo, ele precisa criar uma tabela de funções virtuais (também conhecida como vtable ou vftable) para formar a definição .NET da interface para determinar o código nativo a ser chamado. Esse processo é complexo. Os exemplos a seguir mostram alguns casos simples.
Considere uma interface COM com alguns métodos:
struct IComInterface : public IUnknown
{
STDMETHOD(Method)() = 0;
STDMETHOD(Method2)() = 0;
};
Para essa interface, a tabela a seguir descreve seu layout de tabela de funções virtuais:
IComInterface slot da tabela de funções virtuais |
Nome do método |
|---|---|
| 0 | IUnknown::QueryInterface |
| 1 | IUnknown::AddRef |
| 2 | IUnknown::Release |
| 3 | IComInterface::Method |
| 4 | IComInterface::Method2 |
Cada método é adicionado à tabela de funções virtuais na ordem em que foi declarado. A ordem específica é definida pelo compilador C++, mas para casos simples sem sobrecargas, a ordem de declaração define a ordem na tabela.
Declare uma interface .NET que corresponda a essa interface da seguinte maneira:
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid(/* The IID for IComInterface */)]
interface IComInterface
{
void Method();
void Method2();
}
O InterfaceTypeAttribute especifica a interface base. Ele fornece algumas opções:
| Valor ComInterfaceType | Tipo de interface base | Comportamento dos membros na interface atribuída |
|---|---|---|
InterfaceIsIUnknown |
IUnknown |
A tabela de funções virtuais primeiro apresenta os membros de IUnknown, seguida pelos membros desta interface na ordem de declaração. |
InterfaceIsIDispatch |
IDispatch |
Os membros não são adicionados à tabela de funções virtuais. Eles só são acessíveis através de IDispatch. |
InterfaceIsDual |
IDispatch |
A tabela de funções virtuais primeiro apresenta os membros de IDispatch, seguida pelos membros desta interface na ordem de declaração. |
InterfaceIsIInspectable |
IInspectable |
A tabela de funções virtuais primeiro apresenta os membros de IInspectable, seguida pelos membros desta interface na ordem de declaração. Só há suporte no .NET Framework. |
Herança da interface COM e .NET
O sistema de interoperabilidade COM que usa o ComImportAttribute não interage com a herança da interface, portanto, pode causar um comportamento inesperado, a menos que algumas etapas mitigadoras sejam executadas.
O gerador de origem COM que usa o System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute atributo interage com a herança da interface, portanto, ele se comporta mais conforme o esperado.
Herança da interface COM no C++
No C++, os desenvolvedores podem declarar interfaces COM que derivam de outras interfaces COM da seguinte maneira:
struct IComInterface : public IUnknown
{
STDMETHOD(Method)() = 0;
STDMETHOD(Method2)() = 0;
};
struct IComInterface2 : public IComInterface
{
STDMETHOD(Method3)() = 0;
};
Esse estilo de declaração é usado regularmente como um mecanismo para adicionar métodos a objetos COM sem alterar as interfaces existentes, o que seria uma alteração significativa. Esse mecanismo de herança resulta nos seguintes layouts de tabela de funções virtuais:
IComInterface slot da tabela de funções virtuais |
Nome do método |
|---|---|
| 0 | IUnknown::QueryInterface |
| 1 | IUnknown::AddRef |
| 2 | IUnknown::Release |
| 3 | IComInterface::Method |
| 4 | IComInterface::Method2 |
IComInterface2 slot da tabela de funções virtuais |
Nome do método |
|---|---|
| 0 | IUnknown::QueryInterface |
| 1 | IUnknown::AddRef |
| 2 | IUnknown::Release |
| 3 | IComInterface::Method |
| 4 | IComInterface::Method2 |
| 5 | IComInterface2::Method3 |
Como resultado, é fácil chamar um método definido em IComInterface de um IComInterface2*. Especificamente, chamar um método em uma interface base não requer uma chamada para QueryInterface para poder obter um ponteiro para a interface base. Além disso, o C++ permite uma conversão implícita de IComInterface2* para IComInterface*, que é bem definida e permite evitar chamar uma QueryInterface novamente. Como resultado, em C ou C++, você nunca precisa chamar QueryInterface para chegar ao tipo base se não quiser, o que pode permitir algumas melhorias de desempenho.
Observação
As interfaces WinRT não seguem esse modelo de herança. Eles são definidos para seguir o mesmo modelo que o [ComImport]modelo de interoperabilidade COM baseado no .NET.
Herança de interface com ComImportAttribute
No .NET, o código C# que aparenta ser herança de interface não é, de fato, herança de interface. Considere o seguinte código:
interface I
{
void Method1();
}
interface J : I
{
void Method2();
}
Este código não diz "J implementa I". O código, na verdade, diz: "qualquer tipo que implemente J também deve implementar I." Essa diferença leva à decisão de design fundamental que torna a herança de interfaces em um ambiente de interoperabilidade baseado em ComImportAttribute pouco ergonômica. As interfaces são sempre consideradas por conta própria; A lista de interfaces base de uma interface não tem impacto em nenhum cálculo para determinar uma tabela de funções virtuais para uma determinada interface .NET.
Como resultado, o equivalente natural do exemplo de interface COM do C++ anterior leva a um layout de tabela de funções virtuais diferente.
Código C#:
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface
{
void Method();
void Method2();
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface2 : IComInterface
{
void Method3();
}
Layouts da tabela de funções virtuais:
IComInterface slot da tabela de funções virtuais |
Nome do método |
|---|---|
| 0 | IUnknown::QueryInterface |
| 1 | IUnknown::AddRef |
| 2 | IUnknown::Release |
| 3 | IComInterface::Method |
| 4 | IComInterface::Method2 |
IComInterface2 slot da tabela de funções virtuais |
Nome do método |
|---|---|
| 0 | IUnknown::QueryInterface |
| 1 | IUnknown::AddRef |
| 2 | IUnknown::Release |
| 3 | IComInterface2::Method3 |
Como essas tabelas de funções virtuais diferem do exemplo do C++, isso levará a sérios problemas no runtime. A definição correta dessas interfaces no .NET é a ComImportAttribute seguinte:
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface
{
void Method();
void Method2();
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface2 : IComInterface
{
new void Method();
new void Method2();
void Method3();
}
No nível de metadados, IComInterface2 não implementa IComInterface, mas especifica apenas que os implementadores de IComInterface2 também devem implementar IComInterface. Portanto, cada método dos tipos de interface base deve ser re-declarado.
Herança de interface com GeneratedComInterfaceAttribute (.NET 8 e posterior)
O gerador de origem COM acionado por GeneratedComInterfaceAttribute implementa a herança de interface do C# como herança de interface COM, para que as tabelas de funções virtuais sejam organizadas conforme o esperado. Se você usar o exemplo anterior, a definição correta dessas interfaces no .NET será a System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute seguinte:
[GeneratedComInterface]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface
{
void Method();
void Method2();
}
[GeneratedComInterface]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface2 : IComInterface
{
void Method3();
}
Os métodos das interfaces base não precisam ser redeclarados e não devem ser redeclarados. A tabela a seguir descreve as tabelas de funções virtuais resultantes:
IComInterface slot da tabela de funções virtuais |
Nome do método |
|---|---|
| 0 | IUnknown::QueryInterface |
| 1 | IUnknown::AddRef |
| 2 | IUnknown::Release |
| 3 | IComInterface::Method |
| 4 | IComInterface::Method2 |
IComInterface2 slot da tabela de funções virtuais |
Nome do método |
|---|---|
| 0 | IUnknown::QueryInterface |
| 1 | IUnknown::AddRef |
| 2 | IUnknown::Release |
| 3 | IComInterface::Method |
| 4 | IComInterface::Method2 |
| 5 | IComInterface2::Method3 |
Como você pode ver, essas tabelas correspondem ao exemplo do C++, portanto, essas interfaces funcionarão corretamente.