Compartilhar via


Gerar uma projeção em C# de um componente C++/WinRT, distribuir como um NuGet para aplicativos .NET

Neste tópico, percorremos o uso C#/WinRT para gerar um assembly de projeção do .NET (ou interoperabilidade) em C# de um componente do Windows Runtime do C++/WinRT e distribuí-lo como um pacote NuGet para aplicativos .NET.

No .NET 6 e versões posteriores, o consumo de arquivos de metadados do Windows (WinMD) não é mais suportado (consulte : o suporte interno para WinRT foi removido do .NET). Em vez disso, a ferramenta C#/WinRT pode ser usada para gerar um assembly de projeção para qualquer arquivo WinMD, o que permite o consumo de componentes WinRT de aplicativos .NET. Assemblagem de projeção também é conhecida como assemblagem de interoperabilidade. Este passo a passo mostra como fazer o seguinte:

  • Use o pacote C#/WinRT para gerar uma projeção em C# de um componente C++/WinRT.
  • Distribua o componente, juntamente com o conjunto de projeção, como um pacote NuGet.
  • Consuma o pacote NuGet de um aplicativo de console .NET.

Pré-requisitos

Este passo a passo e o exemplo correspondente exigem as seguintes ferramentas e componentes:

  • Visual Studio 2022 (ou Visual Studio 2019) com a carga de trabalho de desenvolvimento da Plataforma Universal do Windows instalada. Em Detalhes de Instalaçãode desenvolvimento da Plataforma Universal do Windows, verifique a opção de ferramentas da Plataforma Universal do Windows C++ (v14x).
  • SDK do .NET 6.0 ou posterior.

Visual Studio 2019 somente. A extensão C++/WinRT VSIX, a qual oferece modelos de projeto C++/WinRT no Visual Studio. Os modelos de projeto são integrados ao Visual Studio 2022.

Usaremos o Visual Studio 2022 e o .NET 6 neste passo a passo.

Importante

Além disso, você precisará baixar ou clonar o código de exemplo relacionado a este tópico do exemplo de projeção C#/WinRT no GitHub. Visite CsWinRTe clique no botão verde Código para obter a URL git clone. Certifique-se de ler o arquivo README.md como exemplo.

Criar um simples componente do Windows Runtime em C++/WinRT

Para seguir este passo a passo, primeiro você deve ter um WRC (componente do Windows Runtime) do C++/WinRT do qual gerar o assembly de projeção em C#.

Este passo a passo usa o SimpleMathComponent WRC da amostra de projeção C#/WinRT no GitHub, que você já baixou ou clonou. SimpleMathComponent foi criado a partir do modelo de projeto Windows Runtime Component (C++/WinRT) do Visual Studio (que vem com o Visual Studio 2022 ou com a extensão C++/WinRT VSIX).

Para abrir o projeto SimpleMathComponent no Visual Studio, abra o \CsWinRT\src\Samples\NetProjectionSample\CppWinRTComponentProjectionSample.sln arquivo, que você encontrará no download ou clone do repositório.

O código neste projeto fornece a funcionalidade para as operações matemáticas básicas mostradas no arquivo de cabeçalho abaixo.

// SimpleMath.h
...
namespace winrt::SimpleMathComponent::implementation
{
    struct SimpleMath: SimpleMathT<SimpleMath>
    {
        SimpleMath() = default;
        double add(double firstNumber, double secondNumber);
        double subtract(double firstNumber, double secondNumber);
        double multiply(double firstNumber, double secondNumber);
        double divide(double firstNumber, double secondNumber);
    };
}

Você pode confirmar que a propriedade Compatível com a Área de Trabalho do Windows está configurada para Sim no projeto de componente do Windows Runtime C++/WinRT SimpleMathComponent. Para fazer isso, nas propriedades do projeto para SimpleMathComponent, em Propriedades de Configuração>>GeraisPadrões do Projeto, defina a propriedade Compatível com a Área de Trabalho do Windows para Sim. Isso garante que os binários de runtime corretos sejam carregados para consumir aplicativos da área de trabalho do .NET.

Página de propriedades compatível com desktop

Para ver etapas mais detalhadas sobre como criar um componente de C++/WinRT e gerar um arquivo WinMD, consulte Componentes do Windows Runtime com C++/WinRT.

Observação

Se você estiver implementando IInspectable::GetRuntimeClassName em seu componente, ele deve retornar um nome de classe WinRT válido. Como C#/WinRT usa a cadeia de caracteres de nome de classe para interoperabilidade, um nome de classe incorreto em tempo de execução gerará um InvalidCastException.

Adicionar um projeto de projeção à solução de componente

Primeiro, com a solução CppWinRTComponentProjectionSample ainda aberta no Visual Studio, remova o projeto SimpleMathProjection dessa solução. Em seguida, exclua do sistema de arquivos a pasta SimpleMathProjection (ou renomeie-a se preferir). Essas etapas são necessárias para que você possa seguir este passo a passo.

  1. Adicione um novo projeto de biblioteca C# à sua solução.

    1. No Gerenciador de Soluções, clique com o botão direito do mouse no nó da solução e clique em Adicionar>Novo Projeto.
    2. Na caixa de diálogo Adicionar um novo projeto, digite Biblioteca de Classes na caixa de pesquisa. Escolha C# na lista de idiomas e escolha o Windows na lista de plataformas. Escolha o modelo de projeto do C# simplesmente chamado Biblioteca de Classes (sem qualquer prefixo ou sufixo) e clique em Avançar.
    3. Nomeie o novo projeto SimpleMathProjection. O local provavelmente já está configurado para a mesma pasta \CsWinRT\src\Samples\NetProjectionSample na qual a pasta SimpleMathComponent está; mas confirme. Em seguida, clique em Próximo.
    4. Na página Informações adicionais , selecione .NET 6.0 (suporte a longo prazo) e, em seguida, escolha Criar.
  2. Exclua o stub Class1.cs arquivo do projeto.

  3. Use as etapas abaixo para instalar o pacote NuGet do C#/WinRT.

    1. Nodo Gerenciador de Soluções , clique com o botão direito do mouse em seu projeto SimpleMathProjection e selecione Gerenciar Pacotes NuGet.
    2. Na aba Procurar, digite ou cole Microsoft.Windows.CsWinRT na caixa de pesquisa. Nos resultados da pesquisa, selecione o item com a versão mais recente e clique em Instalar para instalar o pacote no projeto SimpleMathProjection.
  4. Adicione a SimpleMathProjection uma referência de projeto ao projeto SimpleMathComponent . Em Gerenciador de Soluções, clique com o botão direito do mouse no nó Dependências sob o nó do projeto SimpleMathProjection, selecione Adicionar Referência do Projetoe escolha o projeto SimpleMathComponent. Clique em >OK.

Não tente criar o projeto ainda. Faremos isso mais tarde.

Até agora, seu Gerenciador de Soluções deve estar semelhante a isto (os números de versão serão diferentes).

Gerenciador de Soluções mostrando dependências de projeto de projeção

Criar projetos a partir do código-fonte

Para a solução de CppWinRTComponentProjectionSample no de exemplo de projeção C#/WinRT do (que você baixou ou clonou do GitHub e agora abriu), o local de saída do build é configurado com o arquivo Directory.Build.props para criar fora dode origem. Isso significa que os arquivos da saída de build são gerados fora da pasta de origem. Recomendamos que você compile fora do código-fonte ao usar a ferramenta C#/WinRT. Isso impede que o compilador C# pegue inadvertidamente todos os arquivos *.cs no diretório raiz do projeto, o que pode causar erros de tipo duplicados (por exemplo, ao compilar para várias configurações e/ou plataformas).

Embora isso já esteja configurado para a solução CppWinRTComponentProjectionSample, siga as etapas abaixo para praticar a configuração por conta própria.

Para configurar sua solução para compilar fora do código-fonte:

  1. Com a solução CppWinRTComponentProjectionSample ainda aberta, clique com o botão direito do mouse no nó da solução e selecione AdicionarNovo Item. Selecione o item Arquivo XML e nomeie-o Directory.Build.props (sem uma extensão .xml). Clique em Sim para substituir o arquivo existente.

  2. Substitua o conteúdo do Directory.Build.props pela configuração abaixo.

    <Project>
      <PropertyGroup>
        <BuildOutDir>$([MSBuild]::NormalizeDirectory('$(SolutionDir)', '_build', '$(Platform)', '$(Configuration)'))</BuildOutDir>
        <OutDir>$([MSBuild]::NormalizeDirectory('$(BuildOutDir)', '$(MSBuildProjectName)', 'bin'))</OutDir>
        <IntDir>$([MSBuild]::NormalizeDirectory('$(BuildOutDir)', '$(MSBuildProjectName)', 'obj'))</IntDir>
      </PropertyGroup>
    </Project>
    
  3. Salve e feche o arquivo Directory.Build.props .

Editar o arquivo de projeto para executar C#/WinRT

Antes de invocar a cswinrt.exe ferramenta para gerar o assembly de projeção, primeiro edite o arquivo de projeto para especificar algumas propriedades do projeto.

  1. No Gerenciador de Soluções, clique duas vezes no nó SimpleMathProjection para abrir o arquivo de projeto no editor.

  2. Atualize o TargetFramework elemento para direcionar uma versão específica do SDK do Windows. Isso adiciona dependências de assembly necessárias para o suporte à interoperabilidade e à projeção. Este exemplo tem como destino a versão do SDK do Windows net6.0-windows10.0.19041.0 (também conhecida como Windows 10, versão 2004). Defina o elemento Platform como AnyCPU para que o assembly de projeção resultante possa ser referenciado de qualquer arquitetura de aplicativo. Para permitir que os aplicativos de referência ofereçam suporte a versões anteriores do SDK do Windows, você também pode definir a TargetPlatformMinimumVersion propriedade.

    <PropertyGroup>
      <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
      <!-- Set Platform to AnyCPU to allow consumption of the projection assembly from any architecture. -->
      <Platform>AnyCPU</Platform>
    </PropertyGroup>
    

    Observação

    Para este passo a passo e o código de exemplo relacionado, a solução é criada para x64 e release. Observe que o projeto SimpleMathProjection está configurado para compilar para AnyCPU para todas as configurações de arquitetura de solução.

  3. Adicione um segundo PropertyGroup elemento (imediatamente após o primeiro) que define várias propriedades C#/WinRT.

    <PropertyGroup>
      <CsWinRTIncludes>SimpleMathComponent</CsWinRTIncludes>
      <CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
    </PropertyGroup>
    

    Aqui estão alguns detalhes sobre as configurações neste exemplo:

    • A CsWinRTIncludes propriedade especifica quais namespaces projetar.
    • A CsWinRTGeneratedFilesDir propriedade define o diretório de saída no qual os arquivos de origem de projeção são gerados. Essa propriedade é definida como OutDir, definida em Directory.Build.props da seção acima.
  4. Salve e feche o arquivo SimpleMathProjection.csproj e clique para Recarregar projetos, se necessário.

Criar um pacote NuGet com a projeção

Para distribuir o assembly de projeção para desenvolvedores de aplicativos .NET, você pode criar automaticamente um pacote NuGet ao criar a solução adicionando algumas propriedades de projeto adicionais. Nos destinos do .NET, o pacote NuGet precisa incluir a biblioteca de projeção e a biblioteca de implementação do componente.

  1. Use as etapas abaixo para adicionar um arquivo de especificação do NuGet (.nuspec) ao projeto SimpleMathProjection .

    1. No Gerenciador de Soluções , clique com o botão direito do mouse no nó SimpleMathProjection, escolha AdicionarNova Pastae nomeie a pasta nuget .
    2. Clique com o botão direito do mouse na pasta do nuget , escolha Adicionarde Novo Item, escolha de arquivo XML e nomeie-o SimpleMathProjection.nuspec.
  2. No Gerenciador de Soluções, clique duas vezes no nó SimpleMathProjection para abrir o arquivo de projeto no editor. Adicione o seguinte grupo de propriedades ao agora aberto SimpleMathProjection.csproj (imediatamente após os dois elementos PropertyGroup existentes) para a geração automática do pacote. Essas propriedades especificam o NuspecFile e o diretório para gerar o pacote NuGet.

    <PropertyGroup>
      <GeneratedNugetDir>.\nuget\</GeneratedNugetDir>
      <NuspecFile>$(GeneratedNugetDir)SimpleMathProjection.nuspec</NuspecFile>
      <OutputPath>$(GeneratedNugetDir)</OutputPath>
      <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
    </PropertyGroup>
    

    Observação

    Se preferir gerar um pacote separadamente, você também poderá optar por executar a nuget.exe ferramenta na linha de comando. Para obter mais informações sobre como criar um pacote NuGet, consulte Criar um pacote usando a CLI nuget.exe.

  3. Abra o arquivo SimpleMathProjection.nuspec para editar as propriedades de criação do pacote e cole o código a seguir. O snippet a seguir é um exemplo de especificação do NuGet para distribuir SimpleMathComponent para várias estruturas de destino. Observe que o conjunto de projeção, SimpleMathProjection.dll, é especificado em vez de SimpleMathComponent.winmd para o destino lib\net6.0-windows10.0.19041.0\SimpleMathProjection.dll. Esse comportamento é novo no .NET 6 e posterior e é habilitado pelo C#/WinRT. A montagem de implementação, SimpleMathComponent.dll, também deve ser distribuída e será carregada em tempo de execução.

    <?xml version="1.0" encoding="utf-8"?>
    <package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
      <metadata>
        <id>SimpleMathComponent</id>
        <version>0.1.0-prerelease</version>
        <authors>Contoso Math Inc.</authors>
        <description>A simple component with basic math operations</description>
        <dependencies>
          <group targetFramework="net6.0-windows10.0.19041.0" />
          <group targetFramework=".NETCoreApp3.0" />
          <group targetFramework="UAP10.0" />
          <group targetFramework=".NETFramework4.6" />
        </dependencies>
      </metadata>
      <files>
        <!--Support .NET 6, .NET Core 3, UAP, .NET Framework 4.6, C++ -->
        <!--Architecture-neutral assemblies-->
        <file src="..\..\_build\AnyCPU\Release\SimpleMathProjection\bin\SimpleMathProjection.dll" target="lib\net6.0-windows10.0.19041.0\SimpleMathProjection.dll" />
        <file src="..\..\_build\x64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.winmd" target="lib\netcoreapp3.0\SimpleMathComponent.winmd" />
        <file src="..\..\_build\x64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.winmd" target="lib\uap10.0\SimpleMathComponent.winmd" />
        <file src="..\..\_build\x64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.winmd" target="lib\net46\SimpleMathComponent.winmd" />
        <!--Architecture-specific implementation DLLs should be copied into RID-relative folders-->
        <file src="..\..\_build\x64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.dll" target="runtimes\win10-x64\native\SimpleMathComponent.dll" />
        <!--To support x86 and Arm64, build SimpleMathComponent for those other architectures and uncomment the entries below.-->
        <!--<file src="..\..\_build\Win32\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.dll" target="runtimes\win10-x86\native\SimpleMathComponent.dll" />-->
        <!--<file src="..\..\_build\arm64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.dll" target="runtimes\win10-arm64\native\SimpleMathComponent.dll" />-->
      </files>
    </package>
    

    Observação

    SimpleMathComponent.dll, o conjunto de implementação do componente, é específico da arquitetura. Se você estiver dando suporte a outras plataformas (por exemplo, x86 ou Arm64), primeiro crie SimpleMathComponent para as plataformas desejadas e adicione esses arquivos de assembly à pasta apropriada relativa a RID. A montagem de projeção SimpleMathProjection.dll e o componente SimpleMathComponent.winmd são neutros em termos de arquitetura.

  4. Salve e feche os arquivos que você acabou de editar.

Criar a solução para gerar a projeção e o pacote NuGet

Antes de compilar a solução, verifique as configurações do Configuration Manager no Visual Studio, em Build>Configuration Manager. Para este passo a passo, defina o de Configuração do para de versão e Platform para x64 para a solução.

Neste ponto, agora você pode criar a solução. Clique com o botão direito do mouse no nó da solução e selecione Criar solução. Isso criará primeiro o projeto SimpleMathComponent e, em seguida, o projeto SimpleMathProjection. O componente WinMD e o assembly de implementação (SimpleMathComponent.winmd e SimpleMathComponent.dll), os arquivos de origem de projeção e o assembly de projeção (SimpleMathProjection.dll), serão gerados todos no diretório de saída _build. Você também poderá ver o pacote NuGet gerado, SimpleMathComponent0.1.0-prerelease.nupkg, na pasta \SimpleMathProjection\nuget .

Importante

Se qualquer um dos arquivos mencionados acima não for gerado, crie a solução uma segunda vez. Talvez você também precise fechar e reabrir a solução antes de recompilar.

Talvez seja necessário fechar e reabrir a solução para que ela .nupkg apareça no Visual Studio conforme ilustrado (ou apenas selecione e desmarque Mostrar Todos os Arquivos).

Solution Explorer exibindo geração de projeção

Referenciar o pacote NuGet em um aplicativo de console .NET 6 em C#

Para consumir SimpleMathComponent de um projeto .NET, basta adicionar ao novo projeto .NET uma referência ao pacote NuGet SimpleMathComponent0.1.0-prerelease.nupkg que criamos na seção anterior. As etapas a seguir demonstram como fazer isso criando um aplicativo de Console simples em uma solução separada.

  1. Use as etapas abaixo para criar uma nova solução que contenha um projeto de Aplicativo de Console em C# (a criação desse projeto em uma nova solução permite que você restaure o pacote NuGet SimpleMathComponent de forma independente).

    Importante

    Criaremos um novo projeto de Aplicativo de Console dentro da pasta \CsWinRT\src\Samples\NetProjectionSample, que você encontrará no seu download ou clone do exemplo de projeção C#/WinRT .

    1. Em uma nova instância do Visual Studio, selecione Arquivo>Nova>Projeto.
    2. Na caixa de diálogo Criar um novo projeto, procure pelo template de projeto Aplicativo de Console. Escolha o modelo de projeto do C# chamado simplesmente Aplicativo de Console (sem prefixos nem sufixos) e clique em Avançar. Se você estiver usando o Visual Studio 2019, o modelo de projeto será o Aplicativo de Console.
    3. Nomeie o novo projeto SampleConsoleApp, defina seu local como a mesma pasta de \CsWinRT\src\Samples\NetProjectionSample em que as pastas SimpleMathComponent e SimpleMathProjection estão e clique em Próximo.
    4. Na página Informações adicionais , selecione .NET 6.0 (suporte a longo prazo) e, em seguida, escolha Criar.
  2. Em do Gerenciador de Soluções, clique duas vezes no nó SampleConsoleApp para abrir o arquivo de projeto SampleConsoleApp.csproj e edite as propriedades TargetFramework e Platform para que fiquem conforme mostrado na listagem a seguir. Adicione o Platform elemento se ele não estiver lá.

    <PropertyGroup>
      <OutputType>Exe</OutputType>
      <TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
      <Platform>x64</Platform>
    </PropertyGroup>
    
  3. Com o arquivo de projeto SampleConsoleApp.csproj ainda aberto, adicionaremos ao projeto SampleConsoleApp uma referência ao pacote NuGet SimpleMathComponent . Para restaurar o SimpleMathComponent NuGet ao compilar o projeto, você pode usar a propriedade RestoreSources com o caminho para a pasta nuget na sua solução de componentes. Copie a configuração a seguir e cole-a em SampleConsoleApp.csproj (dentro do Project elemento).

    <PropertyGroup>
      <RestoreSources>
        https://api.nuget.org/v3/index.json;
        ../SimpleMathProjection/nuget
      </RestoreSources>
    </PropertyGroup>
    
    <ItemGroup>
      <PackageReference Include="SimpleMathComponent" Version="0.1.0-prerelease" />
    </ItemGroup>
    

    Importante

    O caminho para o pacote do SimpleMathComponent, conforme mostrado acima, é definido como . Esse caminho está correto, desde que você tenha seguido as etapas neste passo a passo, para que os projetos SimpleMathComponent e SampleConsoleApp estejam na mesma pasta (a NetProjectionSample pasta, nesse caso). Se você tiver feito algo diferente, precisará ajustar esse caminho adequadamente. Como alternativa, você pode adicionar um feed local de pacotes NuGet à sua solução.

  4. Edite o arquivo Program.cs para usar a funcionalidade fornecida pelo SimpleMathComponent.

    var x = new SimpleMathComponent.SimpleMath();
    Console.WriteLine("Adding 5.5 + 6.5 ...");
    Console.WriteLine(x.add(5.5, 6.5).ToString());
    
  5. Salve e feche os arquivos que você acabou de editar e compile e execute o aplicativo de console. Você deve ver a saída abaixo.

    saída do Console NET5

Problemas conhecidos

  • Ao criar o projeto de projeção, você pode ver um erro como: Erro MSB3271 Havia uma incompatibilidade entre a arquitetura do processador do projeto que está sendo criado "MSIL" e a arquitetura do processador, "x86", do arquivo de implementação "..\SimpleMathComponent.dll" para "..\SimpleMathComponent.winmd". Essa incompatibilidade pode causar falhas em tempo de execução. Considere alterar a arquitetura do processador de destino do seu projeto usando o Gerenciador de Configurações para alinhar as arquiteturas do processador entre o projeto e o arquivo de implementação ou escolha um arquivo winmd com um arquivo de implementação que tenha uma arquitetura de processador que corresponda à arquitetura de processador direcionada do seu projeto. Para contornar esse erro, adicione a seguinte propriedade ao arquivo de projeto da biblioteca C#:
    <PropertyGroup>
        <!-- Workaround for MSB3271 error on processor architecture mismatch -->
        <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
    </PropertyGroup>
    

Considerações adicionais

O assembly de projeção (ou interoperabilidade) do C# que mostramos como criar neste tópico é bastante simples: ele não tem dependências em outros componentes. Mas para gerar uma projeção em C# para um componente C++/WinRT que tenha referências a tipos de SDK de Aplicativo do Windows, no projeto de projeção, você precisaria adicionar uma referência ao pacote NuGet do SDK do Aplicativo Windows. Se essas referências estiverem ausentes, você verá erros como "Tipo <T> não pôde ser encontrado".

Outra coisa que fazemos neste tópico é distribuir a projeção como um pacote NuGet. Esse é atualmente necessário.

Recursos