Поделиться через


Создание проекции C# из компонента C++/WinRT в виде NuGet для приложений .NET

В этом разделе мы рассмотрим использование C#/WinRT для создания сборки проекции C# .NET (или взаимодействия) из компонента C++/WinRT среда выполнения Windows и распространения его в виде пакета NuGet для приложений .NET.

В .NET 6 и более поздних версиях использование файлов метаданных Windows (WinMD) больше не поддерживается (см . встроенную поддержку WinRT из .NET). Вместо этого средство C#/WinRT можно использовать для создания сборки проекции для любого файла WinMD, который затем позволяет использовать компоненты WinRT из приложений .NET. Сборка проекции также называется сборкой взаимодействия. В этом пошаговом руководстве показано, как выполнить следующие действия:

  • Использование пакета C#/WinRT, чтобы создать проекцию C# из компонента C++/WinRT.
  • Распространение компонента вместе со сборкой проекции в виде пакета NuGet.
  • Использование пакета NuGet из консольного приложения .NET.

Необходимые компоненты

Для этого пошагового руководства и соответствующего образца требуются следующие средства и компоненты:

  • Visual Studio 2022 (или Visual Studio 2019) с установленной рабочей нагрузкой разработки приложений для универсальной платформы Windows. В разделе Сведения об установке>Разработка приложений для универсальной платформы Windows, выберите параметр Инструменты универсальной платформы Windows для C++ (версии 14x).
  • Пакет SDK для .NET 6.0 или более поздней версии.

Только Visual Studio 2019. Расширение C++/WinRT VSIX предоставляет шаблоны проектов C++/WinRT в Visual Studio. Шаблоны проектов встроены в Visual Studio 2022.

В этом пошаговом руководстве мы будем использовать Visual Studio 2022 и .NET 6.

Важно!

Кроме того, вам необходимо загрузить или клонировать пример кода для этого раздела из образца проекции C#/WinRT на GitHub. Перейдите в CsWinRT и нажмите зеленую кнопку Код, чтобы получить git clone URL-адрес. Обязательно прочтите файл README.md для образца.

Создание простого компонента C++/WinRT среды выполнения Windows

Для выполнения этого пошагового руководства необходимо сначала создать компонент C++/WinRT среды выполнения Windows (WRC), на основе которого создается сборка проекции C#.

В этом пошаговом руководстве используется компонент SimpleMathComponent WRC из загруженного или клонированного вами ранее образца проекции C#/WinRT на GitHub. SimpleMathComponent создан на основе шаблона проекта Visual Studio для компонента среды выполнения Windows Runtime Component (C++/WinRT) (предоставляемого с Visual Studio 2022 или с расширением C++/WinRT VSIX).

Чтобы открыть проекцию SimpleMathComponent в Visual Studio, откройте \CsWinRT\src\Samples\NetProjectionSample\CppWinRTComponentProjectionSample.sln файл, который находится в разделе загрузок или клоне репозитория.

Код этой проекции обеспечивает функции для основных математических операций, указанных ниже в файле заголовка ниже.

// 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);
    };
}

Можно подтвердить, что для свойства Совместимо с классическими приложениями Windows установлено значение Да для SimpleMathComponent, проекции компонента C++/WinRT для среды выполнения Windows. Для этого, в свойствах проекта для компонента SimpleMathComponent, в разделе Свойства конфигурации>Общее>Значения по умолчанию для проекта, установите для свойства Совместимо с классическими приложениями Windows параметр Да. Это гарантирует загрузку правильных двоичных файлов среды выполнения для использования классических приложений .NET.

Desktop Compatible property page

Более подробные инструкции по созданию компонента c++/WinRT и файла WinMD см. в разделе компоненты среды выполнения Windows с C++/WinRT.

Примечание.

При реализации команды IInspectable::GetRuntimeClassName в вашем компоненте, она должна вернуть действительное имя класса WinRT. Так как C#/WinRT использует строку имени класса для взаимодействия, неправильное имя класса среды выполнения вызовет InvalidCastException.

Добавление проекта проекции в решение компонента

Первое, не закрывая решение CppWinRTComponentProjectionSample в Visual Studio, удалите из этого решения проект SimpleMathProjection. Затем удалите из своей файловой системы папку SimpleMathProjection (или при желании можете ее переименовать). Выполните эти действия, чтобы иметь возможность следовать инструкциям из этого пошагового руководства.

  1. Добавление в решение нового проекта библиотеки C#.

    1. В обозревателе решений щелкните правой кнопкой мыши по узлу решения и выберите команду Добавить>Новый проект.
    2. В диалоговом окнеДобавить новый проект введите в поле поиска Библиотека классов. Выберите C# в списке языков, затем в списке платформ выберите Windows. Выберите шаблон проекта C# с простым именем Библиотека классов (без префиксов или суффиксов) и нажмите кнопку Далее.
    3. Присвойте новому проекту имя SimpleMathProjection. Расположение должно быть уже задано в той же \CsWinRT\src\Samples\NetProjectionSample папке, которой уже находится папка SimpleMathComponent; но в этом необходимо убедиться. Затем нажмите кнопку Далее.
    4. На странице Дополнительные сведения выберите последовательно .NET 6.0 (долгосрочная техническая поддержка), Создать.
  2. Удалите файл-заглушку Class1.cs из проекта.

  3. Выполните следующие действия, пакет NuGet C#/WinRT.

    1. В обозревателе решений, щелкните правой кнопкой мыши по проекту SimpleMathProjection и выберите Управление пакетами NuGet.
    2. На вкладке Просмотр, введите или вставьте в поле поиска Microsoft.Windows.CsWinRT, в результатах поиска выберите элемент с самой последней версией, затем выберите Установить, чтобы установить пакет в проект SimpleMathProjection.
  4. Добавление в SimpleMathProjection ссылки на проект SimpleMathComponent. В обозревателе решений, щелкните правой кнопкой мыши по узлу Зависимости на узле проекта SimpleMathProjection, затем последовательно выберите Добавить ссылку на проект, проект SimpleMathComponent>OK.

Еще не пытайтесь на этом этапе выполнить сборку проекта. Мы будем делать это позднее.

Сейчас вид вашего обозревателя решений должен быть похож на этот (номер вашей версии будет отличаться).

Solution Explorer showing projection project dependencies

Сборка проектов вне исходного кода

Для решения CppWinRTComponentProjectionSample в образце проекции C#/WinRT, (загруженном или клонированном вами с GitHub и открытом сейчас), расположение выходных файлов настраивается с помощью файла Directory.Build.props для сборки вне исходного кода. Это означает, что файлы из выходных файлов сборки создаются за пределами исходной папки. Рекомендуется создавать сборку вне исходного кода при использовании средства C#/WinRT. Это позволяет компилятору C# случайно выбрать все файлы *.cs в корневом каталоге проекта, что может привести к ошибкам повторяющихся типов (например, при компиляции для нескольких конфигураций и (или) платформ).

Хотя эти параметры уже настроены для решения CppWinRTComponentProjectionSample, выполните следующие действия, чтобы получить практические навыки по самостоятельному выполнению настройки параметров.

Чтобы настроить решение для сборки вне исходного кода:

  1. При открытом решении CppWinRTComponentProjectionSample, щелкните правой кнопкой мыши по узлу решения и выберите Добавить>Новый элемент. Выберите элемент файл XML и присвойте ему имя Directory.Build.props (без .xml расширения). Нажмите кнопку Да, чтобы перезаписать существующий файл.

  2. Замените содержимое файла Directory.Build.props приведенной ниже конфигурацией.

    <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. Сохраните и закройте файл Directory.Build.props.

Изменение файла проекта для выполнения C#/WinRT

Перед вызовом cswinrt.exe средства для создания сборки проекции необходимо сначала изменить файл проекта, указав несколько свойств проекта.

  1. В обозревателе решений, щелкните дважды кнопкой мыши по узлу SimpleMathProjection, чтобы открыть файл проекта в редакторе.

  2. Обновите элемент TargetFramework, чтобы он указывал на определенную версию Windows SDK. При этом добавляются зависимости сборки, необходимые для поддержки взаимодействия и проекции. Этот пример предназначен для windows SDK версии net6.0-windows10.0.0.19041.0 (также называется Windows 10 версии 2004). Platform Задайте для элемента значение AnyCPU, чтобы полученная сборка проекции можно ссылаться на любую архитектуру приложения. Чтобы разрешить ссылки на приложения для поддержки более ранних версий пакета SDK для Windows, можно также задать TargetPlatformMinimumVersion свойство.

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

    Примечание.

    В этом пошаговом руководстве и связанном примере кода решение создается для x64 и выпуска. Обратите внимание, что проект SimpleMathProjection настроен для сборки AnyCPU для всех конфигураций архитектуры решения.

  3. Добавьте второй PropertyGroup элемент (сразу после первого), который определяет несколько свойств C#/WinRT.

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

    Ниже приведены некоторые сведения о параметрах в этом примере:

    • Свойство CsWinRTIncludes указывает, какие пространства имен необходимо проектировать.
    • Свойство CsWinRTGeneratedFilesDir задает выходной каталог, в котором создаются исходные файлы проекции. Для этого свойства задается параметр OutDir, заданный в Directory.Build.props из вышеуказанного раздела.
  4. Сохраните и закройте файл SimpleMathProjection.csproj и в случае необходимости выберите Перезагрузить проекты.

Создание пакета NuGet с помощью проекции

Чтобы распространить сборку проекции для разработчиков приложений .NET, можно автоматически создать пакет NuGet при создании решения, добавив дополнительные свойства проекта. Пакет NuGet для целевых объектов .NET должен содержать сборку проекции и сборку реализации из компонента.

  1. Выполните следующие действия для добавления файла NuGet со спецификациями (.nuspec) в проект SimpleMathProjection.

    1. В обозревателе решений, щелкните правой кнопкой мыши по узлу SimpleMathProjection, выберите Добавить>Новая папка, и присвойте папке имя nuget.
    2. Щелкните правой кнопкой мыши по папке nuget, последовательно выберите Добавить>Новая папка, XML-файл, и присвойте ему имя SimpleMathProjection.nuspec.
  2. В обозревателе решений, щелкните дважды кнопкой мыши по узлу SimpleMathProjection, чтобы открыть файл проекта в редакторе. Добавьте следующую группу свойств в открытый проект SimpleMathProjection.csproj (непосредственно после двух существующих элементов PropertyGroup), чтобы создать пакет автоматически. Эти свойства указывают NuspecFile и каталог для создания пакета NuGet.

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

    Примечание.

    Если вы предпочитаете создать пакет отдельно, можно также выбрать запуск средства nuget.exe из командной строки. Дополнительные сведения о создании пакета NuGet см. в разделе Создание пакета с помощью интерфейса командной строки nuget.exe.

  3. Откройте файл SimpleMathProjection.nuspec, чтобы изменить свойства создания пакета и вставьте следующий код. Приведенный ниже фрагмент кода является примером спецификации NuGet для распространения SimpleMathComponent на несколько целевых платформ. Обратите внимание, что сборка проекции SimpleMathProjection.dll указывается вместо SimpleMathComponent.winmd для целевого объектаlib\net6.0-windows10.0.19041.0\SimpleMathProjection.dll. Это поведение является новым в .NET 6 и более поздних версиях и включено C#/WinRT. Сборка реализации, SimpleMathComponent.dllтакже должна распространяться и загружаться во время выполнения.

    <?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>
    

    Примечание.

    SimpleMathComponent.dll, сборка реализации для компонента зависит от архитектуры. Если вы поддерживаете другие платформы (например, x86 или Arm64), сначала необходимо создать SimpleMathComponent для нужных платформ и добавить эти файлы сборок в соответствующую папку RID-относительных. Сборка проекции SimpleMathProjection.dll и компонент SimpleMathComponent.winmd не зависят от архитектуры.

  4. Сохраните и закройте только что измененные файлы.

Выполните сборку решения для создания проекции и пакета NuGet

Прежде чем выполнять сборку решения, обязательно проверьте параметры Диспетчера конфигураций в Visual Studio, в разделе Сборка>Диспетчер конфигураций. В этом пошаговом руководстве задайте для решения значение Configuration to Release and Platform (Версия и платформа) значение x64.

Теперь можно выполнить сборку решения. Щелкните правой кнопкой мыши по узлу своего решения и выберите Выполнить сборку решения. Это сначала создаст сборку для проекта SimpleMathComponent, затем для проекта SimpleMathProjection. Компонент WinMD и сборка реализации (SimpleMathComponent.winmd и SimpleMathComponent.dll), исходные файлы проекции и сборка проекции (SimpleMathProjection.dll) — все это будет создано в выходном каталоге _build. Вы также сможете просмотреть созданный пакет NuGet SimpleMathComponent0.1.0-prerelease.nupkg в папке \SimpleMathProjection\nuget.

Важно!

Никакие из вышеуказанных файлов не создаются, если сбора решения создается второй раз. Также может потребоваться закрыть и повторно открыть решение перед повторным созданием сборки.

Может потребоваться закрыть и снова открыть решение для .nupkg отображения в Visual Studio, как показано (или просто установите и снимите флажок Показать все файлы).

Solution Explorer showing projection generation

Ссылка на пакет NuGet в консольном приложении C# .NET 6

Для использования SimpleMathComponent из проекта .NET, можно просто добавить в новый проект .NET ссылку на проект NuGet SimpleMathComponent0.1.0-prerelease.nupkg пакет NuGet, созданный нам в предыдущем разделе. На последующих этапах демонстрируется, как это сделать путем создания отдельного простого приложения для консоли.

  1. Выполните следующие действия для создания нового решения, содержащего проект Консольного приложения C# (создание этого проекта в новом решении позволит независимо восстановить пакет NuGet SimpleMathComponent).

    Важно!

    Это новый проект Консольное приложение создается внутри папки \CsWinRT\src\Samples\NetProjectionSample, которую можно найти в загруженном или клонированном образце проекции C#/WinRT.

    1. В новом экземпляре Visual Studio, выберите последовательно Файл>Новый>Проект.
    2. В диалоговом окне Создать новый проект найдите шаблон проекта Консольное приложение. Выберите шаблон проекта C# с простым именем Консольное приложение (без префиксов или суффиксов) и нажмите кнопку Далее. Если вы используете Visual Studio 2019, то в качестве шаблона проекта выберите Консольное приложение.
    3. Присвойте новому проекту имя SampleConsoleApp, задайте его размещение в ту же \CsWinRT\src\Samples\NetProjectionSample папку, в которой расположены папки SimpleMathComponent и SimpleMathProjection, затем нажмите кнопку Далее.
    4. На странице Дополнительные сведения выберите последовательно .NET 6.0 (долгосрочная техническая поддержка), Создать.
  2. В обозревателей решений , щелкните дважды кнопкой мыши по узлу SampleConsoleApp, чтобы открыть файл проекта SampleConsoleApp.csproj и изменить TargetFramework и свойства Platform, чтобы они соответствовали свойствам, приведенным в следующем описании. Добавьте элемент Platform, если он отсутствует.

    <PropertyGroup>
      <OutputType>Exe</OutputType>
      <TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
      <Platform>x64</Platform>
    </PropertyGroup>
    
  3. При открытом файле проекта SampleConsoleApp.csproj, мы добавим в проект SampleConsoleApp ссылку на пакет NuGet SimpleMathComponent. Для восстановления пакета NuGet SimpleMathComponent при выполнении сборки проекта, вы можете использовать свойство RestoreSources с путем к папке nuget в вашем решении для компонента. Скопируйте следующую конфигурацию и вставьте ее в SampleConsoleApp.csproj (внутри элемента Project).

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

    Важно!

    RestoreSources Путь для пакета SimpleMathComponent, указанный выше настроен на ../SimpleMathProjection/nuget. Этот путь является правильным при условии выполнения вами действий, указанных в этом руководстве, чтобы оба проекта и SimpleMathComponent, и SampleConsoleApp были расположены в той же папке (в данном случае в папке NetProjectionSample). Если вы сделали что-то другое, то вам потребуется изменить этот путь соответствующим образом. Вместо этого, вы можете добавить локальный пакет NuGet в свое решение.

  4. Измените файл Program.cs, чтобы использовать функции, предоставляемые SimpleMathComponent.

    var x = new SimpleMathComponent.SimpleMath();
    Console.WriteLine("Adding 5.5 + 6.5 ...");
    Console.WriteLine(x.add(5.5, 6.5).ToString());
    
  5. Сохраните и закройте только что измененные файлы, а затем выполните сборку и запустите консольное приложение. Вы должны увидеть результат, приведенный ниже.

    Console NET5 output

Известные проблемы

  • При создании проекта проекции может появиться ошибка, например: ошибка MSB3271 Произошло несоответствие между архитектурой процессора создаваемого проекта MSIL и архитектурой процессора x86 файла реализации .. \SimpleMathComponent.dll для .. \SimpleMathComponent.winmd". Это несоответствие может привести к сбоям среды выполнения. Рекомендуется изменить целевую архитектуру процессора проекта с помощью Configuration Manager, чтобы выровнять архитектуры процессора между файлом проекта и реализацией, или выбрать winmd-файл с файлом реализации с архитектурой процессора, которая соответствует целевой архитектуре процессора проекта. Чтобы обойти эту ошибку, добавьте следующее свойство в файл проекта библиотеки C#:
    <PropertyGroup>
        <!-- Workaround for MSB3271 error on processor architecture mismatch -->
        <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
    </PropertyGroup>
    

Дальнейшие рекомендации

Сборка проекции C# (или взаимодействие), которую мы показали, как создать в этом разделе довольно просто, у нее нет зависимостей от других компонентов. Но чтобы создать проекцию C# для компонента C++/WinRT с ссылками на типы пакета SDK для приложений Для Windows, в проекте проекции необходимо добавить ссылку на пакет NuGet пакета Sdk для приложений Windows. Если отсутствуют такие ссылки, вы увидите такие ошибки, как "Тип <T> не удалось найти".

Еще одна вещь, которую мы делаем в этом разделе, заключается в распространении проекции в виде пакета NuGet. Сейчас это необходимо.

Ресурсы