创作 C# Windows 运行时组件,便于通过 C++/WinRT 应用使用

本主题指导你完成将简单的 C# 组件添加到 C++/WinRT 项目的过程。

通过 Visual Studio,可轻松地在使用 C# 或 Visual Basic 编写的 Windows 运行时组件 (WRC) 项目中创作和部署自己的自定义 Windows 运行时类型,然后从 C++ 应用程序项目中引用该 WRC,并从该应用程序中使用这些自定义类型。

在内部,Windows 运行时类型可以使用 UWP 应用程序中允许的任何 .NET 功能。

在外部,类型的成员只能为其参数和返回值公开 Windows 运行时类型。 在生成解决方案时,Visual Studio 会生成 .NET WRC 项目,然后执行可创建 Windows 元数据 (.winmd) 文件的生成步骤。 这是你的 Windows 运行时组件 (WRC),即 Visual Studio 在你的应用中包含的组件。

注意

.NET 自动将某些常用的 .NET 类型(例如基元数据类型和集合类型)映射到其 Windows 运行时等效项。 这些 .NET 类型可在 Windows 运行时组件的公共接口中使用,并且将作为相应的 Windows 运行时类型向该组件的用户显示。 请参阅使用 C# 和 Visual Basic 创建 Windows 运行时组件

先决条件

创建空白应用

在 Visual Studio 中,使用空白应用(C++/WinRT) 项目模板创建新项目。 请确保使用的是 (C++/WinRT) 模板,而不是 (通用 Windows) 模板。

将新项目的名称设置为 CppToCSharpWinRT,使文件夹结构与本演练相匹配。

向解决方案添加 C# Windows 运行时组件

在 Visual Studio 中,创建组件项目:在“解决方案资源管理器”中,打开 CppToCSharpWinRT 解决方案的快捷菜单,然后依次选择添加新建项目,将新的 C# 项目添加到解决方案 。 在添加新项目对话框的已安装模板部分中,选择 Visual C#,然后依次选择 Windows通用。 选择 Windows 运行时组件(通用 Windows) 模板,然后输入 SampleComponent 作为项目名称。

注意

新的通用 Windows 平台项目对话框中,选择 Windows 10 创意者更新(10.0; 内部版本 15063) 作为最低版本。 有关详细信息,请参阅下面的应用程序最低版本部分。

添加 C# GetMyString 方法

SampleComponent 项目中,将类的名称从 Class1 更改为 Example。 然后,向该类添加两个简单的成员,一个专用 int 字段和一个名为 GetMyString 的实例方法:

    public sealed class Example
    {
        int MyNumber;

        public string GetMyString()
        {
            return $"This is call #: {++MyNumber}";
        }
    }

注意

默认情况下,该类被标记为公共封装。 必须封装通过你的组件公开的所有 Windows 运行时类。

注意

可选:若要为新添加的成员启用 IntelliSense,请在“解决方案资源管理器”中,打开 SampleComponent 项目的快捷菜单,然后选择生成

从 CppToCSharpWinRT 项目引用 C# SampleComponent

在“解决方案资源管理器”的 C++/WinRT 项目中,打开引用的快捷菜单,然后选择添加引用,打开添加引用对话框。 依次选择“项目”和“解决方案”。 选中 SampleComponent 项目的复选框,然后选择确定来添加引用。

注意

可选:若要为 C++/WinRT 项目启用 IntelliSense,请在“解决方案资源管理器”中,打开 CppToCSharpWinRT 项目的快捷菜单,然后选择生成

编辑 MainPage.h

在 CppToCSharpWinRT 项目中打开 MainPage.h in the CppToCSharpWinRT,然后添加两个项。 首先在 #include 语句的末尾添加 #include "winrt/SampleComponent.h",然后向 MainPage 结构添加 winrt::SampleComponent::Example 字段。

// MainPage.h
...
#include "winrt/SampleComponent.h"

namespace winrt::CppToCSharpWinRT::implementation
{
    struct MainPage : MainPageT<MainPage>
    {
...
        winrt::SampleComponent::Example myExample;
...
    };
}

注意

在 Visual Studio 中,MainPage.h 列于 MainPage.xaml 下。

编辑 MainPage.cpp

MainPage.cpp 中,更改 Mainpage::ClickHandler 实现以调用 C# 方法 GetMyString

void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
    //myButton().Content(box_value(L"Clicked"));

    hstring myString = myExample.GetMyString();

    myButton().Content(box_value(myString));
}

运行项目

现在可以生成并运行该项目了。 每次单击该按钮时,按钮中的数字将递增。

C++/WinRT Windows calling into a C# component screenshot

提示

在 Visual Studio 中,创建组件项目:在“解决方案资源管理器”中,打开 CppToCSharpWinRT 项目的快捷菜单,选择属性,然后在配置属性下选择调试。 如果要同时调试 C#(托管)和 C++(本机)代码,请将调试器类型设置为托管和本机C++ Debugging Properties

应用程序最低版本

C# 项目版本的应用程序最低版本将控制用于编译应用程序的 .NET 版本。 例如,选择 Windows 10 Fall Creators Update (10.0; 内部版本 16299) 或更高版本将启用 .NET Standard 2.0 和 Windows Arm64 处理器支持。

提示

如果不需要 .NET Standard 2.0 或 Arm64 支持,建议使用低于 16299 的应用程序最低版本以避免额外的生成配置。

针对 Windows 10 Fall Creators Update(10.0;内部版本 16299)进行配置

按照以下步骤操作,在从 C++/WinRT 项目引用的 C# 项目中启用 .NET Standard 2.0 或 Windows Arm64 支持。

在 Visual Studio 中,转到“解决方案资源管理器”并打开 CppToCSharpWinRT 项目的快捷菜单。 选择属性,并将通用 Windows 应用最低版本设置为 Windows 10 Fall Creators Update (10.0; 内部版本 16299)(或更高版本)。 对 SampleComponent 项目执行相同的操作。

在 Visual Studio 中,打开 CppToCSharpWinRT 项目的快捷菜单,然后选择卸载项目以在文本编辑器中打开 CppToCSharpWinRT.vcxproj

将以下 XML 复制并粘贴到 CPPWinRTCSharpV2.vcxproj 中的第一个 PropertyGroup

   <!-- Start Custom .NET Native properties -->
   <DotNetNativeVersion>2.2.12-rel-31116-00</DotNetNativeVersion>
   <DotNetNativeSharedLibrary>2.2.8-rel-31116-00</DotNetNativeSharedLibrary>
   <UWPCoreRuntimeSdkVersion>2.2.14</UWPCoreRuntimeSdkVersion>
   <!--<NugetPath>$(USERPROFILE)\.nuget\packages</NugetPath>-->
   <NugetPath>$(ProgramFiles)\Microsoft SDKs\UWPNuGetPackages</NugetPath>
   <!-- End Custom .NET Native properties -->

DotNetNativeVersionDotNetNativeSharedLibraryUWPCoreRuntimeSdkVersion 的值可能因 Visual Studio 版本而异。 若要将它们设置为正确的值,请打开 %ProgramFiles(x86)%\Microsoft SDKs\UWPNuGetPackages 并查看下表中每个值的子目录。 %ProgramFiles(x86)%\Microsoft SDKs\UWPNuGetPackages\Microsoft.Net.Native.Compiler 目录将有一个子目录,其中包含以 2.2 开头的已安装的 .NET Native 版本。 在下例中,它是 2.2.12-rel-31116-00

MSBuild 变量 Directory 示例
DotNetNativeVersion %ProgramFiles(x86)%\Microsoft SDKs\UWPNuGetPackages\Microsoft.Net.Native.Compiler 2.2.12-rel-31116-00
DotNetNativeSharedLibrary %ProgramFiles(x86)%\Microsoft SDKs\UWPNuGetPackages\runtime.win10-x64.microsoft.net.native.sharedlibrary 2.2.8-rel-31116-00
UWPCoreRuntimeSdkVersion %ProgramFiles(x86)%\Microsoft SDKs\UWPNuGetPackages\Microsoft.Net.UWPCoreRuntimeSdk 2.2.14

注意

Microsoft.Net.Native.SharedLibrary 支持多种体系结构。 将 x64 替换为适当的体系结构。 例如,arm64 体系结构在 %ProgramFiles(x86)%\Microsoft SDKs\UWPNuGetPackages\runtime.win10-arm64.microsoft.net.native.sharedlibrary 目录中。

接下来,紧接着第一个 PropertyGroup 之后,添加以下内容(未更改)。

  <!-- Start Custom .NET Native targets -->
  <!-- Import all of the .NET Native / CoreCLR props at the beginning of the project -->
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\Microsoft.Net.UWPCoreRuntimeSdk\$(UWPCoreRuntimeSdkVersion)\build\Microsoft.Net.UWPCoreRuntimeSdk.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x86.Microsoft.Net.UWPCoreRuntimeSdk\$(UWPCoreRuntimeSdkVersion)\build\runtime.win10-x86.Microsoft.Net.UWPCoreRuntimeSdk.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x64.Microsoft.Net.UWPCoreRuntimeSdk\$(UWPCoreRuntimeSdkVersion)\build\runtime.win10-x64.Microsoft.Net.UWPCoreRuntimeSdk.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm.Microsoft.Net.UWPCoreRuntimeSdk\$(UWPCoreRuntimeSdkVersion)\build\runtime.win10-arm.Microsoft.Net.UWPCoreRuntimeSdk.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\Microsoft.Net.Native.Compiler.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x86.Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\runtime.win10-x86.Microsoft.Net.Native.Compiler.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x64.Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\runtime.win10-x64.Microsoft.Net.Native.Compiler.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm.Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\runtime.win10-arm.Microsoft.Net.Native.Compiler.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm64.Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\runtime.win10-arm64.Microsoft.Net.Native.Compiler.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x86.Microsoft.Net.Native.SharedLibrary\$(DotNetNativeSharedLibrary)\build\runtime.win10-x86.Microsoft.Net.Native.SharedLibrary.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x64.Microsoft.Net.Native.SharedLibrary\$(DotNetNativeSharedLibrary)\build\runtime.win10-x64.Microsoft.Net.Native.SharedLibrary.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm.Microsoft.Net.Native.SharedLibrary\$(DotNetNativeSharedLibrary)\build\runtime.win10-arm.Microsoft.Net.Native.SharedLibrary.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm64.Microsoft.Net.Native.SharedLibrary\$(DotNetNativeSharedLibrary)\build\runtime.win10-arm64.Microsoft.Net.Native.SharedLibrary.props" />
  <!-- End Custom .NET Native targets -->

在项目文件的末尾,就在结束 Project 标记之前,添加以下内容(未更改)。

  <!-- Import all of the .NET Native / CoreCLR targets at the end of the project -->
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x86.Microsoft.Net.UWPCoreRuntimeSdk\$(UWPCoreRuntimeSdkVersion)\build\runtime.win10-x86.Microsoft.Net.UWPCoreRuntimeSdk.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x64.Microsoft.Net.UWPCoreRuntimeSdk\$(UWPCoreRuntimeSdkVersion)\build\runtime.win10-x64.Microsoft.Net.UWPCoreRuntimeSdk.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm.Microsoft.Net.UWPCoreRuntimeSdk\$(UWPCoreRuntimeSdkVersion)\build\runtime.win10-arm.Microsoft.Net.UWPCoreRuntimeSdk.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\Microsoft.Net.Native.Compiler.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x86.Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\runtime.win10-x86.Microsoft.Net.Native.Compiler.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x64.Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\runtime.win10-x64.Microsoft.Net.Native.Compiler.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm.Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\runtime.win10-arm.Microsoft.Net.Native.Compiler.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm64.Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\runtime.win10-arm64.Microsoft.Net.Native.Compiler.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x86.Microsoft.Net.Native.SharedLibrary\$(DotNetNativeSharedLibrary)\build\runtime.win10-x86.Microsoft.Net.Native.SharedLibrary.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x64.Microsoft.Net.Native.SharedLibrary\$(DotNetNativeSharedLibrary)\build\runtime.win10-x64.Microsoft.Net.Native.SharedLibrary.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm.Microsoft.Net.Native.SharedLibrary\$(DotNetNativeSharedLibrary)\build\runtime.win10-arm.Microsoft.Net.Native.SharedLibrary.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm64.Microsoft.Net.Native.SharedLibrary\$(DotNetNativeSharedLibrary)\build\runtime.win10-arm64.Microsoft.Net.Native.SharedLibrary.targets" />
  <!-- End Custom .NET Native targets -->

在 Visual Studio 中重载该项目文件。 为此,请在 Visual Studio 解决方案资源管理器中,打开 CppToCSharpWinRT 项目的快捷菜单,然后选择重载项目

针对 .NET Native 进行生成

建议使用针对 .NET Native 生成的 C# 组件来生成和测试应用程序。 在 Visual Studio 中,打开 CppToCSharpWinRT 项目的快捷菜单,然后选择卸载项目以在文本编辑器中打开 CppToCSharpWinRT.vcxproj

接下来,在 C++ 项目文件的“版本和 Arm64 配置”中,将 UseDotNetNativeToolchain 属性设置为 true

在 Visual Studio 解决方案资源管理器中,打开 CppToCSharpWinRT 项目的快捷菜单,然后选择重载项目

  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
...
    <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Platform)'=='Arm64'" Label="Configuration">
    <UseDotNetNativeToolchain Condition="'$(UseDotNetNativeToolchain)'==''">true</UseDotNetNativeToolchain>
  </PropertyGroup>

引用其他 C# NuGet 包

如果 C# 组件正在引用其他 NuGet 包,则应用程序的项目文件可能需要列出 NuGet 包中的文件依赖项作为部署内容。 例如,如果 C# 组件引用 Newtonsoft.Json NuGet 包,则应用程序项目中也该引用此 NuGet 包和文件依赖项。

SampleComponent.csproj 文件中,添加 NuGet 包引用:

    <PackageReference Include="Newtonsoft.Json">
      <Version>13.0.1</Version>
    </PackageReference>

CppToCSharpWinRT 项目中,找到 packages.config 文件并添加响应的 NuGet 引用。 这会在解决方案的包文件夹中安装 NuGet 包。

packages.config 中,添加相同的 NuGet 包引用:

  <package id="Newtonsoft.Json" version="13.0.1" targetFramework="native" developmentDependency="true" />

然后,在应用程序项目文件中添加以下内容来引用解决方案包文件夹中相应的文件依赖项。 例如,在 CppToCSharpWinRT.vcxproj 中添加以下内容:

  <ItemGroup>
    <None Include="..\packages\Newtonsoft.Json.13.0.1\lib\netstandard2.0\Newtonsoft.Json.dll">
      <Link>%(Filename)%(Extension)</Link>
      <DeploymentContent>true</DeploymentContent>
    </None>
  </ItemGroup>