Compartilhar via


Solucionar problemas de referências COM

O COM é uma tecnologia do Windows para definir e operar com objetos e tipos que podem ser consumidos por diversos aplicativos cliente em toda a plataforma Windows. Consulte o COM (Modelo de Objeto de Componente).

Você pode referenciar um componente COM em um projeto .NET, nesse caso, ele deve ser projetado em um assembly gerenciado, conhecido como um assembly de interoperabilidade primário ou PIA. O assembly de interoperabilidade contém tipos gerenciados que correspondem aos tipos de objeto COM (representados por interfaces descritas em bibliotecas de tipos) e encaminha chamadas de API para COM. Para obter informações gerais sobre interoperabilidade COM, consulte Interoperabilidade COM.

Você pode referenciar componentes COM em projetos do .NET Framework ou em projetos do .NET Core (incluindo .NET 5 ou posterior). O Visual Studio fornece maneiras de adicionar referências a objetos COM. Por exemplo, um componente COM que é um controle de interface do usuário do Windows (um controle ActiveX) pode ser apresentado na Caixa de Ferramentas e, quando você o arrasta e o solta no formulário do Windows ou em um formulário do Windows Presentation Foundation (WPF), ele é adicionado como um COMReference arquivo de projeto.

Você também pode adicionar referências COM diretamente no Gerenciador de Soluções. Clique com o botão direito do mouse em Dependências e selecione Adicionar Referência COM.

O MSBuild pode criar os assemblies wrapper para referências COM. Durante um build, a ResolveComReference tarefa roda e utiliza o registro do sistema para localizar quaisquer objetos COM referenciados, gera wrappers chamando tlbimp.exe e os grava no disco na pasta do projeto.

Em seguida, este artigo mostra várias maneiras de referenciar componentes COM e alguns dos possíveis erros que podem ocorrer ao usar cada método.

COMReference

O COMReference tipo de item faz referência ao componente COM usando o registro do sistema. O GUID e a versão são as chaves primárias usadas para localizar o componente.

  <ItemGroup>
    <COMReference Include="MyComLibrary">
      <Guid>{01234567-89AB-CDEF-0123-456789ABCDEF}</Guid>
      <VersionMajor>1</VersionMajor>
      <VersionMinor>0</VersionMinor>
      <Lcid>0</Lcid>
      <WrapperTool>tlbimp</WrapperTool>
    </COMReference>
  </ItemGroup>

</Project>

Como o registro do sistema é usado, os componentes precisam ser registrados no computador de build. Isso funcionará melhor se você compilar somente do Visual Studio em seu computador local, em que controla o que está instalado e pode ter direitos de administrador para gravar no registro. No entanto, quando você cria em um servidor ou em um contêiner, como em cenários de CI/CD, você precisa garantir que os produtos certos estejam instalados no servidor de build e as alterações no servidor, como instalar uma nova versão do Office ou desinstalar um pacote de software, introduzam o risco de interromper o build. Ou pior, não quebrando o build, mas fazendo referência a uma versão diferente do mesmo componente do que aquela para a qual seu código foi escrito.

O outro problema é que o componente COM que você obtém do registro pode ter algumas pequenas diferenças para o componente para o qual o código foi escrito. Depende do editor do componente e se alguma atualização foi instalada que o modificou, sem alterar os números de versão. Esse tipo de incompatibilidade não produziria um erro de build, mas poderia produzir um erro em runtime, se as alterações fossem significativas o suficiente para interromper algo do qual seu aplicativo depende.

Informações gerais sobre como os componentes COM são representados no registro podem ser encontradas no Registro de aplicativos COM.

COMFileReference

O COMFileReference tipo de item faz referência a componentes COM por caminho de arquivo. O registro não é usado no momento da compilação. Essa é uma alternativa importante para COMReference se você não quiser que o processo de build dependa do registro do sistema. Se você usar COMFileReference, não precisará se preocupar com como registrar os componentes COM de que seu aplicativo precisa até que o aplicativo seja instalado e executado no computador do usuário, o que é um grande benefício porque o processo de build pode estar em execução sem os privilégios elevados necessários para gravar no registro.

<ItemGroup>
  <COMFileReference Include="Controls\MyCom32.dll" />
</ItemGroup>

Para evitar completamente a gravação no registro, use COM sem registro com um manifesto.

Ainda pode haver um problema com um componente não corresponder ao que você codificou, se o caminho que você referenciou não estiver sob seu controle. Se um pacote de software atualizar o componente no computador de build, sem atualizar as informações de versão, você poderá descobrir que está carregando um componente diferente com uma possível incompatibilidade. Para evitar isso, você pode usar uma versão em cache conhecida dos binários COM e fazer referência apenas a isso.

Reference

Essa é a maneira recomendada de referenciar componentes COM com versões mais recentes do .NET. Os wrappers de assemblies são pré-gerados, em vez de serem criados em cada sessão de compilação, e os resultados são referenciados diretamente como assemblies gerenciados.

Às vezes, os assemblies de encapsulamento (PIAs) são distribuídos pelo fornecedor dos componentes COM. Se esse for o caso, você poderá referenciá-los diretamente como assemblies gerenciados. Isso não é diferente de referenciar qualquer outro assembly do .NET, exceto que, em runtime, há uma dependência do componente COM sendo instalado e registrado.

Para criar os assemblies de wrapper por conta própria para componentes COM que você deseja usar, use tlbimp.exe ou aximp.exe para controles ActiveX.

Usando esse método, você pode evitar a necessidade de privilégios elevados para gravar no registro do sistema no momento da compilação. Se você gerar seus próprios assemblies wrapper e armazená-los em um lugar que você controlar, talvez em um pacote NuGet ou em uma pasta acessível à sua solução, você poderá ser isolado de alterações que estão além do seu controle.

Bitness

Se o projeto fizer referência a componentes COM de 32 bits, você deverá criar usando MSBuild.exe ou o Visual Studio, não dotnet build. Isso ocorre porque dotnet build executa a versão de 64 bits do MSBuild, que não é capaz de trabalhar com componentes COM de 32 bits.

Os componentes COM já foram compilados para binários de 32 bits. Mais tarde, quando a tecnologia de 64 bits foi introduzida, tornou-se possível compilar um componente COM para binários de 32 e 64 bits. O mesmo componente geralmente está disponível em binários de 32 bits e 64 bits. Nesses casos, o GUID ou CLSID que identifica esse componente exclusivo é o mesmo para binários de 32 bits e 64 bits, mas o registro em si é dividido em seções de 32 bits e 64 bits.

Podem surgir erros se seus projetos não estiverem configurados corretamente para referenciar a arquitetura de bits correta do componente COM que você necessita. Se o componente COM estiver disponível apenas como um binário de 32 bits, seu aplicativo só poderá usá-lo se ele for executado como um processo de 32 bits. Se o assembly do .NET for criado como um assembly de 32 bits, ele poderá referenciar componentes COM de 32 bits. Se ele for criado como um assembly de 64 bits, ele poderá referenciar componentes COM de 64 bits. No entanto, se o assembly for criado como Any CPU, você precisará ter cuidado ao referenciar componentes COM, que não têm o equivalente a Any CPU. Talvez seja melhor criar seu aplicativo em formulários de 32 bits e 64 bits, que fazem referência aos componentes COM corretos, supondo que os componentes COM estejam disponíveis em ambas as formas de bits.

Há outra propriedade Prefer32bit de build (também uma caixa de seleção no Visual Studio) que faz com que um assembly, construído como Any CPU, seja sempre executado como 32 bits em uma máquina de 64 bits. Isso funcionaria para ser executado com componentes COM de 32 bits, mas pode ser enganoso para qualquer pessoa que use o projeto mais tarde.

Você pode usar Condition atributos na PlatformTarget propriedade para referenciar duas formas de bits diferentes de um único componente COM. Por exemplo

<ItemGroup Condition="'$(PlatformTarget)' == 'x86'">
  <COMFileReference Include="Controls\MyCom32.dll" />
</ItemGroup>
<ItemGroup Condition="'$(PlatformTarget)' == 'x64'">
  <COMFileReference Include="Controls\MyCom64.dll" />
</ItemGroup>

Ao compilar para x86, você faz referência à DLL COM de 32 bits, mas quando você cria para x64, faz referência à versão de 64 bits.

Como o MSBuild resolve referências COM

O algoritmo básico é descrito aqui, incluindo a sequência de etapas na resolução de uma referência, quais executáveis são executados (por exemplo) tlbimp.exee quais chamadas à API do Windows são usadas.

No processo de build do SDK padrão do .NET, a tarefa ResolveComReference é chamada nos arquivos de alvos comuns em um alvo chamado ResolveComReferences. O destino é invocado uma vez por projeto e processa todas as referências COM, ambas COMReference e COMFileReference. Para obter mais informações, consulte a tarefa ResolveComReference.

A tarefa percorre a árvore de dependências, tentando resolver todas as referências. A maioria dos erros com referências individuais não é fatal; O MSBuild continua tentando resolver outras referências. Alguns erros serão fatais se afetarem todas as referências igualmente.

Se o componente COM for encontrado no registro ou no sistema de arquivos, o MSBuild normalmente tentará reutilizar assemblies de wrapper criados anteriormente, mas, se necessário, gerará os wrappers. Com as configurações padrão, as bibliotecas de wrapper são geradas pela execução de tlbimp.exe e colocadas em uma pasta dentro da pasta do projeto. O tlbimp.exe está incluído no .NET Framework SDK.

Ao definir propriedades, você pode personalizar os argumentos e as configurações fornecidos para a ResolveComReference tarefa. Você pode configurar se deseja usar assemblies de wrapper já construídos, seja anteriormente ou no cache (caso a opção de cache esteja sendo utilizada). Você pode personalizar a pasta de saída definindo a opção EmbedInteropTypes como True. Essa abordagem insere os tipos projetados na biblioteca ou no executável que está sendo criado, em vez de em um assembly encapsulador separado.

Ferramentas de diagnóstico

Para diagnosticar o erro específico de build, é necessário visualizar os detalhes de entrada da tarefa ResolveComReference que falhou.

Diagnóstico excessivamente detalhado

Você pode definir diagnósticos verbosos usando a opção -v:diag na linha de comando do MSBuild ou no IDE do Visual Studio.

No painel Ferramentas>Opções, expanda a seção Todos os Configurações>Projetos e Soluções>Compilação e Execução e defina as opções de verbosidade da saída de build do projeto MSBuild e a verbosidade do arquivo de log de build do projeto MSBuild como Diagnóstico.

Na caixa de diálogoOpções de >, expanda a seçãoCompilação e Execução de > e defina a verbosidade de saída de build do projeto MSBuild e as opções de verbosidade do arquivo de log de build do projeto MSBuild como Diagnóstico.

Exibir logs binários

Gere um log binário (-bl use a opção na linha de comando do MSBuild) e use o visualizador de log estruturado, que oferece uma interface de usuário que facilita muito ver as etapas detalhadas da compilação, os valores dos parâmetros de entrada das tarefas e assim por diante.

Aqui está a exibição do ResolveComReferences destino no visualizador de log estruturado. Você pode inspecionar os parâmetros e as saídas, que representam os caminhos de referência resolvidos e os assemblies de invólucro. Neste exemplo, o local do assembly do wrapper é expandido para mostrar o local e o nome do arquivo do assembly gerado do wrapper.

Captura de tela do visualizador de log estruturado do MSBuild, examinando o destino ResolveComReferences.

Depois de identificar o nome do componente, o GUID e a versão que produziram uma falha, você poderá examinar todas as propriedades e itens fornecidos à ResolveComReference tarefa e coletar informações sobre esse componente do registro do sistema. Você pode usar o editor regedit.exedo Registro, mas editar o registro requer privilégios de Administrador.

RegEdit

Familiarize-se com os locais do Registro para componentes COM, tanto de 32 bits quanto de 64 bits. GUIDs que identificam um tipo de classe COM são chamados de IDs de classe (CLSID) e armazenados sob CLSID no registro de sistema. Em um computador de 64 bits, os componentes de 64 bits são registrados HKEY_LOCAL_MACHINE\Software\Classes\CLSID\, mas os componentes de 32 bits são registrados em HKEY_LOCAL_MACHINE\Software\WOW6432Node\Classes\CLSID\. Em um computador de 32 bits, os componentes de 32 bits são registrados em HKEY_LOCAL_MACHINE\Software\Classes\CLSID\. Normalmente, você encontra o componente pesquisando seu nome ou GUID no editor do Registro. Se o registro do componente estiver em um hive do Registro, como no caso dos componentes do Visual Studio, talvez seja necessário localizar e abrir o hive. Consulte Editar o registro de uma instância do Visual Studio.

OleView

Talvez você prefira usar oleview.exe para investigar um tipo COM individual e obter informações como a biblioteca de tipos e quais interfaces ela implementa. O OLEView não requer permissões de administrador e é mais fácil de usar do que regedit.exe.

Procmon

Você pode usar o Monitor procmon de Processo para monitorar aplicativos que usam COM em runtime e monitorar as alterações do Registro.

Problemas comuns

Esta seção descreve problemas comuns que podem ocorrer ao usar referências COM.

Problemas de registro

O componente está registrado no computador de build? Se um de seus próprios componentes não estiver registrado, ele poderá ser registrado manualmente usando uma ferramenta de linha de comando. regsvr32.exe (Esse comando requer permissões elevadas no computador.) Se os componentes fizerem parte de um pacote de software, como o Office, verifique se a versão certa do produto que distribui e registra o componente está instalada. Tente reparar ou reinstalar o produto ou pacote de software.

Confirme as propriedades do COMReference. O nome, o GUID e as versões estão corretos, o mesmo que no Registro? Verifique se há erros de digitação, erros de ortografia, incompatibilidades de versão ou outras inconsistências.

Considere se o componente é um componente de 32 bits ou 64 bits, se ambas as versões estiverem disponíveis. Se você estiver direcionando o ARM64, consulte Criar binários Arm64X.

Com COMFileReference, você faz referência a um componente COM pelo local do arquivo. Verifique o caminho para o arquivo e certifique-se de que ele esteja correto, levando em conta o diretório de trabalho atual caso seja um caminho relativo.

Problemas de biblioteca de tipos

Uma biblioteca de tipos é necessária para gerar um assemblagem de interoperabilidade. As bibliotecas de tipos podem ser inseridas em um binário como uma DLL ou podem estar em um arquivo separado, um arquivo TLB (.tlb extensão).

Se você não conseguir encontrar uma biblioteca de tipos para o componente COM, muitas vezes poderá gerar uma. Normalmente, você não precisa gerar uma biblioteca de tipos. Ele deve ser instalado com o componente, distribuído pelo provedor de componentes. Soluções comuns incluem instalar, atualizar ou reinstalar um pacote de software.

Há maneiras de gerar uma biblioteca de tipos de um binário COM no caso raro em que uma é necessária. Por exemplo, você pode abrir o binário no editor de recursos do Visual Studio ou um editor de recursos de terceiros, localizar o recurso de biblioteca de tipos, exportá-lo e usar Salvar Arquivo Como para salvá-lo como um arquivo de texto com a tlb extensão.

Falha ao escanear dependências

MSB3304 ocorrerá se houver algum problema ao verificar as dependências da referência. A ResolveComReference tarefa tenta percorrer todo o grafo de dependências, portanto, qualquer problema de identificação das dependências gera esse erro. A mensagem de erro fornecida depende do problema específico. Se o erro for proveniente tlbimp.exe, você também poderá tentar executar tlbimp.exe na linha de comando para obter mais detalhes sobre o problema.

Problemas ao gerar assemblies de wrapper

O MSBuild tenta encontrar assemblies de wrapper existentes em seu cache de uma execução anterior, se essa opção tiver sido especificada ou reutilizar wrappers gerados anteriormente. Se necessário, o sistema gera o empacotador. A ferramenta tlbimp.exe é usada para criar assemblies de envoltório. Uma pasta é criada para conter os assemblies de wrapper. Por padrão, a pasta é criada na pasta do projeto, mas isso é definido pelo WrapperAssemblyLocation parâmetro da ResolveComReference tarefa. Se esse processo falhar, o erro MSB3290 ou MSB3283 será dado, juntamente com as informações de erro do sistema operacional. Confira as dicas de solução de problemas sugeridas para esses erros específicos.

Sintaxe COMReference ou COMFileReference incorreta

Se o MSBuild não converter os metadados do arquivo de projeto, você receberá o erro MSB3095. Se isso ocorrer, verifique se há erros de digitação ou outros erros nas referências COM (ou quaisquer propriedades usadas nas referências COM) no arquivo de projeto. Verifique os subelementos do COMReference elemento contra os metadados esperados, conforme documentado em itens comuns de projeto do MSBuild, para identificar erros de sintaxe ou metadados ausentes.

Erros de Entrada/Saída do arquivo

A ResolveComReference tarefa lê e grava no sistema de arquivos, o que significa que ela pode falhar com um erro de E/S do sistema, que é capturado pelo MSBuild e relatado em um código de erro genérico do MSBuild. Examine os detalhes do erro nas mensagens do sistema operacional. Verifique a ortografia e a correção de quaisquer caminhos e para os caminhos construídos a partir das propriedades do MSBuild, verifique as propriedades para garantir que elas sejam definidas e tenham os valores esperados.

Warnings

Alguns problemas com a resolução de referência COM são relatados como avisos. Você pode ver MSB3305, que é fornecido quando uma chamada de função subjacente relata um problema não fatal, como um possível problema de conversão de tipo. Você pode suprimir esses avisos definindo a propriedade ResolveComReferenceSilent MSBuild como true. Não recomendamos usar essa configuração permanentemente, mas pode ser útil se você entender o problema e quiser suprimir a notificação temporariamente. Você também pode usar técnicas padrão para suprimir avisos. Consulte Suprimir avisos de build.

Chamadas de método nativo do Windows

O ResolveComReference pode chamar determinadas funções da API do Windows. Erros dessas chamadas de API são passados para a saída de build. Eles estão listados aqui para referência.

Nome da função Description
FreeLibrary Libera o módulo DLL (biblioteca de vínculo dinâmico) carregado.
GetModuleFileName Recupera o caminho totalmente qualificado para o arquivo que contém o módulo especificado.
LoadLibrary Carrega o módulo especificado no espaço de endereço do processo de chamada.
LoadRegTypeLib Usa informações do Registro para carregar uma biblioteca de tipos.
QueryPathOfRegTypeLib Recupera o caminho de uma biblioteca de tipos registrada.