Partilhar via


Gere uma projeção em C# a partir de um componente C++/WinRT, distribua como um NuGet para aplicativos .NET

Neste tópico, explicamos como usar C#/WinRT para gerar um assembly de projeção (ou interoperabilidade) C# .NET a partir de um componente do Tempo de Execução do Windows C++/WinRT e distribuí-lo como um pacote NuGet para aplicativos .NET.

A partir do .NET 6 e versões posteriores, a utilização de ficheiros de metadados do Windows (WinMD) já não é suportada (consulte Remoção do suporte interno para WinRT no .NET). Em vez disso, a ferramenta C#/WinRT pode ser usada para gerar um assembly de projeção para qualquer arquivo WinMD, que permite o consumo de componentes WinRT de aplicativos .NET. Um conjunto de projeção também é conhecido como um conjunto de interoperabilidade. Este passo a passo mostra como fazer o seguinte:

  • Use o pacote C#/WinRT para gerar uma projeção C# a partir de um componente C++/WinRT.
  • Distribua o componente, juntamente com a montagem de projeção, como um pacote NuGet.
  • Consuma o pacote NuGet a partir de uma aplicação de consola .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 da Instalaçãode desenvolvimento da Plataforma Universal do Windows, marque a opção ferramentas da Plataforma Universal do Windows C++ (v14x) .
  • SDK do .NET 6.0 ou posterior.

Visual Studio 2019 apenas. A extensão C++/WinRT VSIX, que fornece modelos de projeto C++/WinRT no Visual Studio. Os modelos de projeto são incorporados ao Visual Studio 2022.

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

Importante

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

Criar um componente simples do Tempo de Execução do Windows em C++/WinRT

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

Este passo a passo usa o SimpleMathComponent WRC do exemplo de projeção C#/WinRT no GitHub, que já foi baixado ou clonado por si. SimpleMathComponent foi criado a partir do modelo de projeto Componente do Tempo de Execução do Windows (C++/WinRT) para Visual Studio, que está disponível 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á em seu 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 Windows Desktop Compatible está definida como Sim para o projeto do componente SimpleMathComponent C++/WinRT Windows Runtime component. Para fazer isso, nas propriedades do projeto para SimpleMathComponent, em Propriedades de Configuração>Geral>, Definições do Projeto, defina a propriedade Windows Desktop Compatible como Sim. Isso garante que os binários de tempo de execução corretos sejam carregados para consumir aplicativos de área de trabalho .NET.

Página de propriedades Desktop Compatible

Para obter etapas mais detalhadas sobre como criar um componente C++/WinRT e gerar um arquivo WinMD, consulte Componentes do Tempo de Execução do Windows com C++/WinRT.

Observação

Se estiver a implementar IInspectable::GetRuntimeClassName no seu componente, então ele deve devolver um nome de classe WinRT válido. Como o C#/WinRT usa a string 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 seu 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 na caixa de pesquisa Biblioteca de Classes. Escolha C# na lista de idiomas e, em seguida, escolha Windows na lista de plataformas. Escolha o modelo de projeto C# chamado simplesmente Biblioteca de Classes (sem prefixos nem sufixos) e clique em Avançar.
    3. Nomeie o novo projeto SimpleMathProjection. O local já deve estar definido para a mesma \CsWinRT\src\Samples\NetProjectionSample pasta em que a pasta SimpleMathComponent está, mas confirme isso. Em seguida, clique em Avançar.
    4. Na página Informações adicionais, selecione .NET 6.0 (Suporte de longo prazo)e, em seguida, clique em Criar.
  2. Exclua o ficheiro stub Class1.cs do projeto.

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

    1. No Explorador de Soluções , clique com o botão direito do rato no projecto SimpleMathProjection e selecione Gerir pacotes NuGet.
    2. Na guia 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. Adicionar ao SimpleMathProjection uma referência de projeto para o projeto SimpleMathComponent. No Explorador 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 de Projeto, e selecione o projeto SimpleMathComponent>OK.

Não tente construir o projeto ainda. Faremos isso em uma etapa posterior.

Até agora, o do Gerenciador de Soluções deve ser semelhante a este (seus números de versão serão diferentes).

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

Crie projetos fora do código-fonte

Para a solução de CppWinRTComponentProjectionSample no de exemplo de projeção C#/WinRT (que você baixou ou clonou do GitHub e agora abriu), o local de saída da compilação é configurado com o arquivo Directory.Build.props para criar fora dode origem. Isso significa que os arquivos da saída de compilação são gerados fora da pasta de origem. Recomendamos que você crie 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).

Mesmo que isso já esteja configurado para a solução CppWinRTComponentProjectionSample , siga as etapas abaixo para obter prática em fazer a configuração por conta própria.

Para configurar sua solução para criar a partir 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 Ficheiro XML e nomeie-o Directory.Build.props (sem extensão .xml). Clique Sim para substituir o arquivo existente.

  2. Substitua os conteúdos de 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 .

Edite o arquivo de projeto para executar C#/WinRT

Antes de poder invocar a cswinrt.exe ferramenta para gerar o assembly de projeção, você deve primeiro editar o arquivo de projeto para especificar algumas propriedades do projeto.

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

  2. Atualize o elemento TargetFramework para direcionar uma versão específica do SDK do Windows. Isso adiciona dependências de assembly que são necessárias para o suporte à interoperabilidade e projeção. Este exemplo destina-se à 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 a partir de qualquer arquitetura de aplicativo. Para permitir que 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 tutorial e o código de exemplo relacionado, a solução foi 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. Esta 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 adicionais do projeto. Para destinos .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 Explorador de Soluções , clique com o botão direito no nó SimpleMathProjection, escolha Adicionar>Nova Pastae dê o nome à pasta nuget.
    2. Clique com o botão direito do mouse na pasta nuget , escolha Adicionar>Novo Item, escolha arquivo XML e nomeie-o SimpleMathProjection.nuspec.
  2. No Explorador de Soluções, clique duas vezes no nó SimpleMathProjection para abrir o ficheiro de projeto no editor. Adicione o seguinte grupo de propriedades ao SimpleMathProjection.csproj agora aberto (imediatamente após os dois elementos existentes PropertyGroup ) para gerar automaticamente o 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 você preferir gerar um pacote separadamente, então você também pode optar por executar a nuget.exe ferramenta a partir da 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 trecho abaixo é um exemplo de especificação NuGet para distribuir SimpleMathComponent para várias estruturas de destino. Observe que a assemblagem de projeção, SimpleMathProjection.dll, é especificada em vez de SimpleMathComponent.winmd para o alvo lib\net6.0-windows10.0.19041.0\SimpleMathProjection.dll. Esse comportamento é novo no .NET 6 e posterior e é habilitado pelo C#/WinRT. A assemblagem 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 para o componente, é específico para a arquitetura. Se estiveres a dar suporte a outras plataformas (por exemplo, x86 ou Arm64), deverás primeiro criar o SimpleMathComponent para as plataformas desejadas e adicionar esses ficheiros de assembly à pasta relativa ao RID apropriada. A montagem de projeção SimpleMathProjection.dll e o componente SimpleMathComponent.winmd são ambos neutros em termos de arquitetura.

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

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

Antes de criar a solução, verifique as configurações de do Configuration Manager no Visual Studio, em BuildConfiguration Manager. Para este passo a passo, defina o de Configuração do como Release 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 Build Solution. Isso primeiro criará o projeto SimpleMathComponent, e, em seguida, o projeto SimpleMathProjection. O componente WinMD e o assembly de implementação (SimpleMathComponent.winmd e SimpleMathComponent.dll), bem como os ficheiros de origem de projeção e o assembly de projeção (SimpleMathProjection.dll), serão todos gerados 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 algum dos arquivos mencionados acima não for gerado, construa a solução uma segunda vez. Também pode ser necessário fechar e reabrir a solução antes de reconstruir.

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

Solution Explorer mostrando a geração de projeção

Fazer referência ao pacote NuGet em um aplicativo de console C# .NET 6

Para consumir SimpleMathComponent de um projeto .NET, pode simplesmente adicionar a um novo projeto .NET uma referência ao pacote do NuGet SimpleMathComponent0.1.0-prerelease.nupkg que criámos 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 contendo um aplicativo de console C# projeto (criar este projeto em uma nova solução permite restaurar o SimpleMathComponent pacote NuGet independentemente).

    Importante

    Criaremos este novo projeto do Console App dentro da pasta , que você encontrará em seude exemplo de projeção C#/WinRT baixado .

    1. Em uma nova instância do Visual Studio, selecione Arquivo>Novo>Projeto.
    2. Na caixa de diálogo Criar um novo projeto, procure o modelo de projeto Console App. Escolha o modelo de projeto C# chamado simplesmente Console App (sem prefixos nem sufixos) e clique em Avançar. Se você estiver usando o Visual Studio 2019, o modelo de projeto será Aplicativo de Console.
    3. Nomeie o novo projeto SampleConsoleApp, defina a sua localização para a mesma pasta \CsWinRT\src\Samples\NetProjectionSample em que as pastas SimpleMathComponent e SimpleMathProjection estão, e clique em Avançar.
    4. Na página Informações adicionais, selecione .NET 6.0 (Suporte de longo prazo)e, em seguida, clique em Criar.
  2. No Gerenciador de Soluções, clique duas vezes no nó SampleConsoleApp para abrir o arquivo de projeto SampleConsoleApp.csproj e edite as TargetFramework propriedades e Platform para que tenham a aparência mostrada 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, em seguida, adicionaremos ao projeto SampleConsoleApp uma referência ao pacote SimpleMathComponent NuGet. Para restaurar o SimpleMathComponent NuGet ao criar o projeto, pode utilizar a propriedade RestoreSources com o caminho para a pasta NuGet na sua solução de componente. 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 SimpleMathComponent mostrado acima está definido como . Esse caminho está correto, desde que tenhas seguido os passos deste tutorial, de modo que os projetos SimpleMathComponent e SampleConsoleApp estejam ambos na mesma pasta (neste caso, a pasta NetProjectionSample). Se fizeste algo diferente, então precisarás ajustar o caminho em conformidade. Como alternativa, você pode adicionar um de feed de pacote NuGet local à 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 crie e execute o aplicativo de console. Deverá ver o resultado abaixo.

    Console NET5 saída

Problemas conhecidos

  • Ao criar o projeto de projeção, você pode ver um erro como: Erro MSB3271 Houve uma incompatibilidade entre a arquitetura do processador do projeto que está sendo construído "MSIL" e a arquitetura do processador, "x86", do arquivo de implementação "..\SimpleMathComponent.dll" para ".. \SimpleMathComponent.winmd". Essa incompatibilidade pode causar falhas de tempo de execução. Considere alterar a arquitetura do processador de destino do seu projeto através do Configuration Manager 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 do processador de destino do seu projeto. Para contornar esse erro, adicione a seguinte propriedade ao seu arquivo de projeto de biblioteca C#:
    <PropertyGroup>
        <!-- Workaround for MSB3271 error on processor architecture mismatch -->
        <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
    </PropertyGroup>
    

Outras considerações

O assembly de projeção (ou interoperabilidade) em C# que mostramos como criar neste tópico é bastante simples — 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 aplicativos Windows, no projeto de projeção você precisa adicionar uma referência ao pacote NuGet do SDK de aplicativos Windows. Se alguma dessas referências estiver faltando, 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 é necessário neste momento.

Recursos