Expor componentes do .NET Core ao COM

Este artigo explica como expor uma classe ao COM do .NET Core (ou .NET 5+). Este tutorial mostra como:

  • Expor uma classe ao COM do .NET Core.
  • Gerar um servidor COM como parte de criar sua biblioteca do .NET Core.
  • Gerar automaticamente um manifesto de servidor lado a lado para COM Sem Registro.

Pré-requisitos

Criar a biblioteca

A primeira etapa é criar a biblioteca.

  1. Crie uma pasta e nela execute o seguinte comando:

    dotnet new classlib
    
  2. Abra o Class1.cs.

  3. Adicione using System.Runtime.InteropServices; ao topo do arquivo.

  4. Crie uma interface chamada IServer. Por exemplo:

    using System;
    using System.Runtime.InteropServices;
    
    [ComVisible(true)]
    [Guid(ContractGuids.ServerInterface)]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IServer
    {
        /// <summary>
        /// Compute the value of the constant Pi.
        /// </summary>
        double ComputePi();
    }
    
  5. Adicione o atributo [Guid("<IID>")] à interface, com o GUID da interface COM que você está implementando. Por exemplo, [Guid("fe103d6e-e71b-414c-80bf-982f18f6c1c7")]. Observe que esse GUID precisa ser exclusivo, pois é o único identificador dessa interface para COM. No Visual Studio, você pode gerar um GUID acessando Ferramentas > Criar GUID para abrir a ferramenta Criar GUID.

  6. Adicione o atributo [InterfaceType] à interface e especifique quais interfaces COM base sua interface deve implementar.

  7. Crie uma classe chamada Server que implementa IServer.

  8. Adicione o atributo [Guid("<CLSID>")] à classe, com o GUID do identificador de classe da classe COM que você está implementando. Por exemplo, [Guid("9f35b6f5-2c05-4e7f-93aa-ee087f6e7ab6")]. Como ocorre com o GUID da interface, esse GUID deve ser exclusivo pois é o único identificador dessa interface para COM.

  9. Adicione o atributo [ComVisible(true)] à interface e à classe.

Importante

Diferentemente do .NET Framework, o .NET Core exige que você especifique o CLSID de qualquer classe que você deseja que seja ativável por meio do COM.

Gerar o host COM

  1. Abra o arquivo de projeto .csproj e adicione <EnableComHosting>true</EnableComHosting> dentro de uma marca <PropertyGroup></PropertyGroup>.
  2. Compile o projeto.

A saída resultante terá um arquivo ProjectName.dll, ProjectName.deps.json, ProjectName.runtimeconfig.json e ProjectName.comhost.dll.

Registrar o host COM para COM

Abra um prompt de comandos com privilégios elevados e execute regsvr32 ProjectName.comhost.dll. Isso registrará todos os seus objetos .NET expostos com o COM.

Habilitar o COM RegFree

  1. Abra o arquivo de projeto .csproj e adicione <EnableRegFreeCom>true</EnableRegFreeCom> dentro de uma marca <PropertyGroup></PropertyGroup>.
  2. Compile o projeto.

A saída resultante agora também terá um arquivo ProjectName.X.manifest. Esse arquivo é o manifesto lado a lado para uso com o COM Sem Registro.

Inserindo bibliotecas de tipos no host COM

Ao contrário do .NET Framework, não há suporte no .NET Core ou no .NET 5+ para gerar uma TLB (Biblioteca de Tipos) COM por meio de um assembly do .NET. A orientação é escrever manualmente um arquivo IDL ou um cabeçalho C/C++ para as declarações nativas das interfaces COM. Se você decidir escrever um arquivo IDL, poderá compilá-lo com o compilador MIDL do SDK do Visual C++ para produzir uma TLB.

No .NET 6 e em versões posteriores, o SDK do .NET dá suporte à inserção de TLBs já compiladas no host COM como parte da compilação do projeto.

Para inserir uma biblioteca de tipos em seu aplicativo, siga estas etapas:

  1. Abra o arquivo de projeto .csproj e adicione <ComHostTypeLibrary Include="path/to/typelib.tlb" Id="<id>" /> dentro de uma marca <ItemGroup></ItemGroup>.
  2. Substitua <id> por um valor inteiro positivo. O valor deve ser exclusivo entre as TLBs especificadas para sere inserido no host COM.
    • O atributo Id será opcional se você adicionar apenas um ComHostTypeLibrary ao seu projeto.

Por exemplo, o seguinte bloco de código adiciona a biblioteca de tipos Server.tlb do índice 1 ao host COM:

<ItemGroup>
    <ComHostTypeLibrary Include="Server.tlb" Id="1" />
</ItemGroup>

Carregar no AssemblyLoadContext padrão

Durante a ativação, o assembly que contém o componente COM é carregado em um AssemblyLoadContext separado com base no caminho do assembly. Se houver um assembly que forneça vários servidores COM, AssemblyLoadContext será reutilizado de modo que todos os servidores desse assembly residam no mesmo contexto de carga. Se houver vários assemblies fornecendo servidores COM, um novo AssemblyLoadContext será criado para cada assembly e cada servidor residirá no contexto de carga que corresponde ao assembly dele.

No .NET 8 e versões posteriores, o assembly pode especificar que ele deve ser carregado no AssemblyLoadContext padrão. Para habilitar o carregamento no contexto padrão, adicione o seguinte item RuntimeHostConfigurationOption ao projeto:

<ItemGroup>
  <RuntimeHostConfigurationOption Include="System.Runtime.InteropServices.COM.LoadComponentInDefaultContext" Value="true" />
</ItemGroup>

Amostra

Há um exemplo de servidor COM totalmente funcional no repositório dotnet/de exemplos no GitHub.

Observações adicionais

Importante

Em .NET Framework, um assembly "Any CPU" pode ser consumido por clientes de 32 bits e de 64 bits. Por padrão, no .NET Core, no .NET 5 e em versões posteriores, os assemblies "Any CPU" são acompanhados por uma *.comhost.dll de 64 bits. Por isso, eles só podem ser consumidos por clientes de 64 bits. Esse é o padrão porque é isso que o SDK representa. Esse comportamento é idêntico à forma como o recurso "autossuficiente" é publicado: por padrão, ele usa o que o SDK fornece. A propriedade MSBuild de NETCoreSdkRuntimeIdentifier determina o número de bit da *.comhost.dll. A parte gerenciada é realmente agnóstica do número de bit, conforme o esperado, mas o ativo nativo que a acompanha assume como padrão o SDK de destino.

Não há suporte para implantações autossuficientes de componentes COM. Há suporte apenas para as implantações dependentes de estrutura de componentes COM.

Além disso, carregar .NET Framework e .NET Core no mesmo processo tem limitações de diagnóstico. A principal limitação é a depuração de componentes gerenciados, pois não é possível depurar o .NET Framework e o .NET Core ao mesmo tempo. Além disso, as duas instâncias de runtime não compartilham assemblies gerenciados. Isso significa que não é possível compartilhar tipos reais do .NET entre os dois runtimes e, em vez disso, todas as interações devem ser restritas aos contratos de interface COM expostos.