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.
Quando um cliente COM chama um objeto .NET, o Common Language Runtime cria o objeto gerenciado e um CCW (COM Callable Wrapper) para o objeto. Não é possível fazer referência diretamente a um objeto .NET, os clientes COM usam o CCW como um proxy para o objeto gerenciado.
O runtime cria exatamente um CCW para um objeto gerenciado, independentemente do número de clientes COM que solicitam seus serviços. Como mostra a ilustração a seguir, vários clientes COM podem conter uma referência ao CCW que expõe a interface INew. O CCW, por sua vez, contém uma única referência ao objeto gerenciado que implementa a interface e é coletada como lixo. Os clientes COM e .NET podem fazer solicitações no mesmo objeto gerenciado simultaneamente.
Os COM Callable Wrappers são invisíveis para outras classes em execução no runtime do .NET. Sua finalidade principal é realizar marshaling de chamadas entre o código gerenciado e não gerenciado; no entanto, os CCWs também gerenciam a identidade e o tempo de vida dos objetos gerenciados encapsulados por eles.
Identidade do objeto
O runtime aloca memória para o objeto .NET em seu heap coletado como lixo, que permite ao runtime mover o objeto na memória, conforme necessário. Por outro lado, o runtime aloca memória para o CCW em um heap não coletado, possibilitando que os clientes COM referenciem o wrapper diretamente.
Tempo de vida do objeto
Ao contrário do cliente .NET encapsulado por ele, o CCW é contado por referência no modo tradicional do COM. Quando a contagem de referência do CCW chega a zero, o wrapper libera sua referência no objeto gerenciado. Um objeto gerenciado sem referências restantes é coletado durante o próximo ciclo de coleta de lixo.
Simulando interfaces COM
O CCW expõe todas as interfaces públicas visíveis para o COM, os tipos de dados e os valores retornados para clientes COM de uma maneira consistente com a exigência do COM em relação à interação baseada em interface. Para um cliente COM, invocar métodos em um objeto .NET é idêntico a invocar métodos em um objeto COM.
Para criar essa abordagem perfeita, o CCW fabrica interfaces COM tradicionais, como IUnknown e IDispatch. Como mostra a ilustração a seguir, o CCW mantém uma única referência no objeto .NET que ele encapsula. O cliente COM e o objeto .NET interagem entre si por meio da construção de proxy e stub do CCW.
Além de expor as interfaces que são explicitamente implementadas por uma classe no ambiente gerenciado, o runtime do .NET fornece implementações das interfaces COM listadas na tabela a seguir em nome do objeto. Uma classe .NET pode substituir o comportamento padrão fornecendo sua própria implementação dessas interfaces. No entanto, o runtime sempre fornece a implementação para as interfaces IUnknown e IDispatch .
| Interfase | Descrição |
|---|---|
| de IDispatch | Fornece um mecanismo de associação tardia ao tipo. |
| IErrorInfo | Fornece uma descrição textual do erro, sua origem, um arquivo de Ajuda, contexto de Ajuda e o GUID da interface que definiu o erro (sempre GUID_NULL para classes .NET). |
| IProvideClassInfo | Permite que os clientes COM obtenham acesso à interface ITypeInfo implementada por uma classe gerenciada. Retorna COR_E_NOTSUPPORTED no .NET Core para tipos não importados do COM. |
| ISupportErrorInfo | Permite que um cliente COM determine se o objeto gerenciado dá suporte à interface IErrorInfo . Nesse caso, permite que o cliente obtenha um ponteiro para o objeto de exceção mais recente. Todos os tipos gerenciados dão suporte à interface IErrorInfo . |
| ITypeInfo (somente .NET Framework) | Fornece informações de tipo para uma classe exatamente igual às informações de tipo produzidas pelo Tlbexp.exe. |
| IUnknown | Fornece a implementação padrão da interface IUnknown com a qual o cliente COM gerencia o tempo de vida do CCW e fornece a coerção de tipo. |
Uma classe gerenciada também pode fornecer as interfaces COM descritas na tabela a seguir.
| Interfase | Descrição |
|---|---|
| A interface da classe (_classname) | Interface, exposta pelo runtime e não definida explicitamente, que expõe todas as interfaces públicas, métodos, propriedades e campos que são explicitamente expostos em um objeto gerenciado. |
| IConnectionPoint e IConnectionPointContainer | Interface para objetos que dão origem a eventos baseados em representante (uma interface para o registro de assinantes do evento). |
| IDispatchEx (somente .NET Framework) | Interface fornecida pelo runtime se a classe implementar IExpando. A interface IDispatchEx é uma extensão da interface IDispatch que, ao contrário de IDispatch, permite a enumeração, adição, exclusão e chamada de membros que diferencia maiúsculas de minúsculas. |
| IEnumVARIANT | Interface para classes de tipo de coleção, que enumera os objetos na coleção se a classe implementa IEnumerable. |
Introdução à interface de classe
A interface de classe, que não é explicitamente definida no código gerenciado, é uma interface que expõe todos os métodos públicos, propriedades, campos e eventos que são explicitamente expostos no objeto .NET. Essa interface pode ser uma interface dupla ou somente de expedição. A interface de classe recebe o nome da própria classe .NET, precedida por um sublinhado. Por exemplo, para a classe Mammal, a interface de classe é _Mammal.
Para classes derivadas, a interface de classe também expõe todos os métodos públicos, propriedades e campos da classe base. A classe derivada também expõe uma interface de classe para cada classe base. Por exemplo, se a classe Mammal estender a classe MammalSuperclass, que por si só estende System.Object, o objeto .NET exporá a clientes COM três interfaces de classe chamadas _Mammal, _MammalSuperclass e _Object.
Por exemplo, considere a seguinte classe .NET:
' Applies the ClassInterfaceAttribute to set the interface to dual.
<ClassInterface(ClassInterfaceType.AutoDual)> _
' Implicitly extends System.Object.
Public Class Mammal
Sub Eat()
Sub Breathe()
Sub Sleep()
End Class
// Applies the ClassInterfaceAttribute to set the interface to dual.
[ClassInterface(ClassInterfaceType.AutoDual)]
// Implicitly extends System.Object.
public class Mammal
{
public void Eat() {}
public void Breathe() {}
public void Sleep() {}
}
O cliente COM pode obter um ponteiro para uma interface de classe chamada _Mammal. No .NET Framework, você pode usar a ferramenta Exportador de Biblioteca de Tipos (Tlbexp.exe) para gerar uma biblioteca de tipos que contém a definição de _Mammal interface. Não há suporte para o Exportador de Biblioteca de Tipos no .NET Core. Se a Mammal classe implementasse uma ou mais interfaces, as interfaces apareceriam na coclasse.
[odl, uuid(…), hidden, dual, nonextensible, oleautomation]
interface _Mammal : IDispatch
{
[id(0x00000000), propget] HRESULT ToString([out, retval] BSTR*
pRetVal);
[id(0x60020001)] HRESULT Equals([in] VARIANT obj, [out, retval]
VARIANT_BOOL* pRetVal);
[id(0x60020002)] HRESULT GetHashCode([out, retval] short* pRetVal);
[id(0x60020003)] HRESULT GetType([out, retval] _Type** pRetVal);
[id(0x6002000d)] HRESULT Eat();
[id(0x6002000e)] HRESULT Breathe();
[id(0x6002000f)] HRESULT Sleep();
}
[uuid(…)]
coclass Mammal
{
[default] interface _Mammal;
}
Gerar a interface de classe é opcional. Por padrão, a interoperabilidade COM gera uma interface somente de expedição para cada classe exportada para uma biblioteca de tipos. Você pode impedir ou modificar a criação automática dessa interface aplicando-a ClassInterfaceAttribute à sua classe. Embora a interface de classe possa facilitar a tarefa de expor classes gerenciadas ao COM, seus usos são limitados.
Cuidado
Usar a interface de classe, em vez de definir explicitamente a sua própria, pode complicar o controle de versão futuro de sua classe gerenciada. Leia as diretrizes a seguir antes de usar a interface de classe.
Defina uma interface explícita para os clientes COM usarem em vez de gerar a interface de classe.
Como a interoperabilidade COM gera uma interface de classe automaticamente, as alterações pós-versão em sua classe podem alterar o layout da interface de classe exposta pelo common language runtime. Como clientes COM normalmente não estão preparados para lidar com alterações no layout de uma interface, eles falham se você alterar o layout dos membros da classe.
Essa diretriz reforça a noção de que as interfaces expostas aos clientes COM devem permanecer imutáveis. Para reduzir o risco de interromper os clientes COM reordenando inadvertidamente o layout da interface, isole todas as alterações na classe do layout da interface definindo interfaces explicitamente.
Use o ClassInterfaceAttribute para desativar a geração automática da interface de classe e implementar uma interface explícita para a classe, como mostra o seguinte fragmento de código:
<ClassInterface(ClassInterfaceType.None)>Public Class LoanApp
Implements IExplicit
Sub M() Implements IExplicit.M
…
End Class
[ClassInterface(ClassInterfaceType.None)]
public class LoanApp : IExplicit
{
int IExplicit.M() { return 0; }
}
O valor ClassInterfaceType.None impede que a interface de classe seja gerada quando os metadados de classe são exportados para uma biblioteca de tipos. No exemplo anterior, os clientes COM só podem acessar a LoanApp classe por meio da IExplicit interface.
Evitar o armazenamento em cache dos identificadores de despacho (DispIds)
O uso da interface de classe é uma opção aceitável para os clientes com script, os clientes do Microsoft Visual Basic 6.0 ou qualquer cliente de associação tardia que não armazena em cache os DispIds dos membros da interface. Os DispIds identificam os membros da interface para habilitar a associação tardia.
Para a interface de classe, a geração de DispIds baseia-se na posição do membro na interface. Se você alterar a ordem do membro e exportar a classe para uma biblioteca de tipos, você alterará as DispIds geradas na interface de classe.
Para evitar a interrupção de clientes COM de associação tardia ao usar a interface de classe, aplique o ClassInterfaceAttribute ao valor ClassInterfaceType.AutoDispatch. Esse valor implementa uma interface de classe somente de expedição, mas omite a descrição da interface na biblioteca de tipos. Sem uma descrição de interface, os clientes não podem armazenar dispIds em cache em tempo de compilação. Embora esse seja o tipo de interface padrão para a interface de classe, você pode aplicar o valor do atributo explicitamente.
<ClassInterface(ClassInterfaceType.AutoDispatch)> Public Class LoanApp
Implements IAnother
Sub M() Implements IAnother.M
…
End Class
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class LoanApp
{
public int M() { return 0; }
}
Para obter o DispId de um membro de interface em runtime, os clientes COM podem chamar IDispatch.GetIdsOfNames. Para invocar um método na interface, passe o DispId retornado como um argumento para IDispatch.Invoke.
Restrinja o uso da opção de interface dupla para a interface de classe.
Interfaces duplas permitem a associação inicial e tardia a membros da interface por clientes COM. Em tempo de design e durante o teste, você pode achar útil definir a interface de classe como dupla. Para uma classe gerenciada (e suas classes base) que nunca serão modificadas, essa opção também é aceitável. Em todos os outros casos, evite definir a interface de classe como dupla.
Uma interface dupla gerada automaticamente pode ser apropriada em casos raros; no entanto, com mais frequência, ele cria complexidade relacionada à versão. Por exemplo, os clientes COM que usam a interface de classe de uma classe derivada podem ser facilmente interrompidos com alterações na classe base. Quando um terceiro fornece a classe base, o layout da interface da classe fica fora do seu controle. Além disso, ao contrário de uma interface somente de expedição, uma interface dupla (ClassInterfaceType.AutoDual) fornece uma descrição da interface de classe na biblioteca de tipos exportada. Uma descrição como essa incentiva os clientes de associação tardia a armazenarem em cache os DispIds em tempo de compilação.
Verifique se todas as notificações de evento COM têm associação tardia.
Por padrão, informações de tipo COM são incorporadas diretamente em assemblies gerenciados, o que elimina a necessidade de PIAS (assemblies de interoperabilidade primários). No entanto, uma das limitações das informações de tipo inseridas é que elas não são compatíveis com a entrega de notificações de eventos COM por chamadas early-bound (vtable), mas apenas com chamadas IDispatch::Invoke de associação tardia.
Se seu aplicativo exigir chamadas com vinculação antecipada para métodos de interface de evento COM, você poderá definir a propriedade Inserir Tipos de Interoperabilidade no Visual Studio para true, ou incluir o seguinte elemento em seu arquivo de projeto:
<EmbedInteropTypes>True</EmbedInteropTypes>