Partilhar via


Solucionar problemas de referências COM

COM é uma tecnologia do Windows para definir e operar com objetos e tipos que podem ser consumidos por diversos aplicativos cliente na plataforma Windows. Consulte Modelo de objeto componente (COM).

Você pode referenciar um componente COM num projeto .NET; nesse caso, ele deve ser incorporado a um assembly gerido, designado como um assembly de interoperabilidade primário ou PIA. O assembly de interoperabilidade contém tipos gerenciados que correspondem a tipos de objeto COM (conforme 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.

Pode referenciar componentes COM em projetos .NET Framework, ou em projetos .NET Core (incluindo .NET 5 ou posteriores). Visual Studio fornece maneiras de adicionar referências a objetos COM. Por exemplo, um componente COM que é um controle de interface do Windows (um controle ActiveX) pode ser apresentado na caixa de ferramentas e, quando o arrasta e larga para o formulário do Windows Forms ou num formulário do Windows Presentation Foundation (WPF), é adicionado como um COMReference no ficheiro do 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.

MSBuild pode criar assemblies de encapsulamento para referências COM. Durante uma compilação, a tarefa é executada ResolveComReference e usa o registro do sistema para localizar quaisquer objetos COM referenciados, gera wrappers chamando tlbimp.exee grava-os 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 devem ser registrados na máquina de compilação. Isto funciona melhor se só compilares a partir do Visual Studio na tua máquina local, onde controlas o que está instalado e podes ter direitos de administrador para escrever no registo. 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 corretos sejam instalados no servidor de compilação, e as alterações no servidor, como a instalação de uma nova versão do Office ou a desinstalação de um pacote de software, introduzem o risco de quebrar a compilação. Ou pior, não quebrando a compilação, mas fazendo referência a uma versão diferente do mesmo componente 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 seu código foi escrito. Depende do editor do componente e se foram instaladas atualizações que o modificaram, sem alterar os números de versão. Esse tipo de incompatibilidade não produziria um erro de compilação, mas poderia produzir um erro em tempo de execução, se as alterações fossem significativas o suficiente para quebrar algo do qual seu aplicativo depende.

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

COMFileReference

O COMFileReference tipo de item faz referência a componentes COM por caminho de arquivo. O registo não é usado durante a compilação. Esta é uma alternativa importante para COMReference se você não quiser que seu processo de compilação dependa do registro do sistema. Se usar COMFileReference, não precisa se preocupar em como registar os componentes COM necessários para o seu aplicativo até que este seja instalado e executado na máquina do utilizador, o que é uma grande vantagem, pois o seu processo de build pode estar a ser executado sem os privilégios elevados necessários para gravar no registo.

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

Para evitar gravar no registo, use COM sem registo com um manifesto.

Ainda pode haver um problema com um componente não ser igual ao que codificaste, se o caminho referenciado não estiver sob o teu controlo. Se um pacote de software atualizar o componente na máquina de compilação, sem atualizar as informações de versão, você pode achar que está carregando um componente diferente com uma possível incompatibilidade. Para evitar isso, você pode usar uma versão em cache e conhecida dos binários COM e apenas fazer referência a isso.

Reference

Esta é a maneira recomendada de fazer referência a componentes COM com versões mais recentes do .NET. Os assemblies wrapper são pregenerados, em vez de serem criados em cada sessão de compilação, e as saídas são referenciadas diretamente como assemblies gerenciados.

Às vezes, conjuntos de wrapper (PIAs) são distribuídos pelo provedor dos componentes COM. Se esse for o caso, você pode fazer referência a eles diretamente como assemblies gerenciados. Isso não é diferente de fazer referência a qualquer outro assembly .NET, exceto que, em tempo de execução, há uma dependência do componente COM, que precisa estar instalado e registado.

Para criar os assemblies wrapper manualmente para componentes COM que pretende utilizar, use tlbimp.exe ou aximp.exe para controlo 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 conjuntos de wrapper e armazená-los em um local que você controla, talvez em um pacote NuGet ou em uma pasta acessível à sua solução, você pode ser isolado de alterações que estão além do seu controle.

Bitness

Se seu projeto faz referência a componentes COM de 32 bits, você deve criar usando MSBuild.exe ou Visual Studio, não dotnet build. Isto porque dotnet build corre a versão de 64 bits do MSBuild, que não é capaz de funcionar com componentes COM de 32 bits.

Os componentes COM já foram todos compilados em 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 bits 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 próprio registo é bifurcado em secções distintas de 32 bits e 64 bits.

Erros podem surgir se seus projetos não estiverem configurados corretamente para fazer referência ao número de bits correto do componente COM de que você precisa. Se o componente COM estiver disponível apenas como um binário de 32 bits, seu aplicativo só poderá usá-lo se for executado como um processo de 32 bits. Se o assembly .NET foi criado como um assembly de 32 bits, ele pode referenciar componentes COM de 32 bits. Se for construído como um assembly de 64 bits, ele pode fazer referência a componentes COM de 64 bits. No entanto, se o seu conjunto é construído como Any CPU, você tem que ter cuidado ao referenciar componentes COM, que não têm o equivalente Any CPUa . Pode ser melhor criar o seu aplicativo em versões de 32 bits e 64 bits, que fazem referência aos componentes COM corretos, assumindo que os componentes COM estejam disponíveis em ambas as arquiteturas.

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 execute sempre como 32 bits, numa 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 fazer referência a duas formas de bitness 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>

Quando você cria para x86, você faz referência a DLL COM de 32 bits, mas quando você cria para x64, você 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.exe) e quais chamadas de API do Windows são usadas.

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

A tarefa percorre a árvore de dependência, tentando resolver todas as referências. A maioria dos erros com referências individuais não são fatais; O MSBuild continua a tentar resolver outras referências. Alguns erros sã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 tenta reutilizar assemblies wrapper criados anteriormente, mas, se necessário, ele gera os wrappers. Com as configurações padrão, os assemblies wrapper são gerados ao executar tlbimp.exe e colocados numa pasta debaixo da pasta do projeto. O tlbimp.exe está incluído no SDK do .NET Framework.

Ao definir propriedades, você pode personalizar os argumentos e as configurações fornecidas para a ResolveComReference tarefa. Você pode configurar se deseja usar assemblies de wrapper criados anteriormente ou no cache (se a opção de cache estiver a ser usada). Podes personalizar a pasta de saída definindo a opção EmbedInteropTypes para True. Esta abordagem incorpora os tipos projetados na biblioteca ou executável que está a ser construído, em vez de num conjunto separado de wrappers.

Ferramentas de diagnóstico

Para diagnosticar o erro de compilação específico, você precisa ver a entrada detalhada para a ResolveComReference tarefa que falhou.

Diagnóstico verboso

Pode definir diagnósticos detalhados usando o -v:diag interruptor na linha de comandos do MSBuild ou no IDE do Visual Studio.

No painel Ferramentas>Opções, expanda a secção Todos os Definições>Projetos e Soluções>Construir e Executar, e defina as opções de verbosidade da saída de construção do projeto MSBuild e verbosidade do ficheiro de registo do projeto MSBuild para Diagnóstico.

No diálogo Ferramentas>Opções, expanda a secção Projetos e Soluções>Compilar e Executar, e defina as opções de verbosidade da saída de compilação do projeto MSBuild e verbosidade do ficheiro de log de compilação do projeto MSBuild para Diagnóstico.

Visualizar logs binários

Gere um log binário (-bl ative a linha de comando MSBuild) e use o visualizador de log estruturado, que fornece uma interface do usuário que torna muito mais fácil ver as etapas detalhadas na compilação, os valores dos parâmetros de entrada da tarefa e assim por diante.

Aqui está a exibição do ResolveComReferences alvo no visualizador de log estruturado. Você pode inspecionar os parâmetros e saídas, que representam os caminhos de referência resolvidos e os assemblies de empacotador. Neste exemplo, a localização da assemblagem do wrapper é expandida para mostrar o local e o nome do ficheiro da assemblagem do wrapper gerada.

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

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

RegEdit

Familiarize-se com os locais de registro dos componentes COM, tanto de 32 bits como de 64 bits. Os GUIDs que identificam um tipo de classe COM são designados como IDs de classe (CLSID) e são armazenados sob CLSID no registo. Em uma máquina de 64 bits, os componentes de 64 bits são registrados em HKEY_LOCAL_MACHINE\Software\Classes\CLSID\, mas os componentes de 32 bits são registrados em HKEY_LOCAL_MACHINE\Software\WOW6432Node\Classes\CLSID\. Em uma máquina de 32 bits, os componentes de 32 bits são registrados em HKEY_LOCAL_MACHINE\Software\Classes\CLSID\. Você geralmente encontra o componente procurando por seu nome ou GUID no editor do Registro. Se o registro do componente estiver em uma seção do Registro, como no caso dos componentes do Visual Studio, talvez seja necessário localizar e abrir a colmeia. Consulte Editando o registro para uma instância do Visual Studio.

OleView

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

Procmon

Você pode usar o Process Monitor procmon para monitorar aplicativos que usam COM em tempo de execução e monitorar alterações no Registro.

Problemas comuns

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

Questões de registo

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

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

Considere se o componente é de 32 bits ou 64 bits, se ambas as versões estiverem disponíveis. Se estiveres a trabalhar com ARM64, consulta Criação de binários Arm64X.

Com COMFileReference, faz-se referência a um componente COM pela sua localização no ficheiro. Verifique o caminho para o arquivo e certifique-se de que está correto, contabilizando o diretório de trabalho atual se for um caminho relativo.

Problemas com a biblioteca de tipos

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

Se não conseguir encontrar uma biblioteca de tipos para o componente COM, muitas vezes você pode gerar uma. Normalmente, não é necessário gerar uma biblioteca de tipos. Ele deve ser instalado com o componente, distribuído pelo provedor do componente. As soluções comuns incluem a instalação, atualização ou reinstalação de um pacote de software.

Há maneiras de gerar uma biblioteca de tipos a partir de um binário COM no caso raro em que um é necessário. Por exemplo, você pode abrir o binário no editor de recursos do Visual Studio ou em um editor de recursos de terceiros, localizar o recurso da 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 ocorre se houver algum problema na verificação das dependências da referência. A tarefa ResolveComReference tenta percorrer todo o gráfico de dependências, portanto, qualquer problema ao identificar as dependências resulta nesse erro. A mensagem de erro fornecida depende do problema específico. Se o erro vier de tlbimp.exe, pode também tentar executar tlbimp.exe na linha de comando para obter mais detalhes sobre o problema.

Problemas na geração de montagens de wrapper

O MSBuild tenta localizar assemblies wrapper existentes em seu cache de uma execução anterior, se essa opção foi especificada, ou reutilizar wrappers gerados anteriormente. Se necessário, gera a embalagem. A ferramenta tlbimp.exe é utilizada para construir assemblies de wrapper. Uma pasta é criada para conter os assemblies de encapsulamento. Por padrão, a pasta é criada na pasta do projeto, mas isso é definido pelo WrapperAssemblyLocation parâmetro da ResolveComReference tarefa. Se este processo falhar, o código de erro MSB3290 ou MSB3283 é apresentado, juntamente com as informações de erro do sistema operativo. Consulte as dicas de solução de problemas sugeridas para esses erros específicos.

Sintaxe COMReference ou COMFileReference incorreta

Se o MSBuild não conseguir converter os metadados do arquivo de projeto, você receberá um 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 elemento em relação aos COMReference metadados esperados, conforme documentado em itens de projeto comuns do MSBuild, em busca de erros de sintaxe ou metadados ausentes.

Erros de entrada/saída de ficheiro

A tarefa ResolveComReference lê e grava no sistema de ficheiros, o que significa que pode ocasionar um erro de E/S do sistema, que é capturado pelo MSBuild e relatado sob um código de erro genérico da MSBuild. Observe os detalhes do erro nas mensagens do sistema operacional. Verifica a ortografia e correção de quaisquer caminhos, e para caminhos construídos a partir das propriedades do MSBuild, verifica as propriedades para garantir que estão definidas e têm os valores esperados.

Warnings

Alguns problemas com a resolução de referência COM são relatados como avisos. Pode ver MSB3305, que é dado quando uma chamada de função subjacente reporta 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 o uso dessa configuração permanentemente, mas ela 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. Veja Suprimir avisos de build.

Chamadas de método nativo do Windows

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

Nome da função Description
FreeLibrary Libera o módulo de biblioteca de vínculo dinâmico (DLL) 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.