本主题逐步讲解如何使用 C#/WinRT 从 C++/WinRT Windows 运行时 组件生成 C# .NET投影(或互操作)程序集,并将其分发为 .NET 应用程序的 NuGet 包。
在 .NET 6 及更高版本中,不再支持对 Windows 元数据(WinMD)文件的使用(请参阅 内置的 WinRT 支持已从 .NET 中移除)。 相反,C#/WinRT 工具可用于为任何 WinMD 文件生成投影程序集,这样就可以从.NET应用程序中使用 WinRT 组件。 投影程序集也称为互操作程序集。 本演练演示如何执行以下操作:
- 使用 C#/WinRT 包从 C++/WinRT 组件生成 C# 投影。
- 将组件以及投影程序集作为 NuGet 包分发。
- 在 .NET 控制台应用程序中使用 NuGet 包。
先决条件
本演练和相应的示例需要以下工具和组件:
- Visual Studio 2022 或更高版本安装了通用 Windows 平台开发工作负载。 在 Installation Details>通用 Windows 平台 development 中,选择 C++ (v14x) 通用 Windows 平台 tools 选项。
- .NET 8.0 SDK (LTS) 或更高版本。
在本演练中,我们将使用 Visual Studio 2022 或更高版本和 .NET 8。
重要
此外,还需要从 GitHub 上的 C#/WinRT 投影示例下载或克隆本主题的示例代码。 访问 CsWinRT,然后单击绿色 代码 按钮获取 git clone URL。 请务必查看示例的 README.md 文件。
创建简单的 C++/WinRT Windows 运行时 组件
若要遵循本演练,必须首先具有用于生成 C# 投影程序集的 C++/WinRT Windows 运行时组件(WRC)。
本演练使用已下载或克隆的 GitHub C#/WinRT 投影示例中的 SimpleMathComponent WRC。 SimpleMathComponent是从 Windows 运行时 组件(C++/WinRT) Visual Studio项目模板创建的。
若要在 Visual Studio 中打开 SimpleMathComponent 项目,请打开 \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 Desktop Compatible 属性已为 SimpleMathComponent C++/WinRT Windows 运行时 组件项目设置为 是。 为此,在 SimpleMathComponent 的 project 属性中, 在 Configuration Properties>General>Project Defaults, 将属性 Windows Desktop Compatible 设置为 Yes。 这可确保加载正确的运行时二进制文件以使用.NET桌面应用。
有关创建 C++/WinRT 组件和生成 WinMD 文件的更详细步骤,请参阅Windows 运行时包含 C++/WinRT 的组件。
注释
如果要在组件中实现 IInspectable::GetRuntimeClassName,则 必须 返回有效的 WinRT 类名。 由于 C#/WinRT 使用类名字符串进行互操作,因此不正确的运行时类名将引发 InvalidCastException。
将投影项目添加到组件解决方案
首先,请在 Visual Studio 中仍然打开 CppWinRTComponentProjectionSample 解决方案,并从中删除 SimpleMathProjection 项目。 然后从文件系统中删除 SimpleMathProjection 文件夹(或根据需要重命名)。 为了让你能够逐步跟随这个演练,这些步骤是必不可少的。
向解决方案添加新的 C# 库项目。
- 在 解决方案资源管理器 中,右键单击解决方案节点,然后单击 Add>New Project。
- 在“添加新项目 对话框中,在搜索框中键入 类库。 从语言列表中选择 C#,然后从平台列表中选择Windows。 选择名称为 类库(没有前缀和后缀)的 C# 项目模板,然后单击 "下一步"。
- 将新项目命名 SimpleMathProjection。 该位置应已设置为
\CsWinRT\src\Samples\NetProjectionSample文件夹所在的同一 文件夹,但请确认。 然后单击“下一步”。 - 在 Additional information 页上,选择 .NET 8.0 (长期支持),然后选择 Create。
从项目中删除存根 Class1.cs 文件。
使用以下步骤安装 C#/WinRT NuGet 包。
- 在 解决方案资源管理器 中,右键单击 SimpleMathProjection 项目,然后选择 Manage NuGet Packages。
- 在 Browse 选项卡中,键入或粘贴 Microsoft.Windows。CsWinRT在搜索框中,在搜索结果中选择具有最新版本的项目,然后单击install将包安装到 SimpleMathProjection 项目中。
将对 SimpleMathComponent 项目的引用添加到 SimpleMathProjection 中。 在 解决方案资源管理器 中,右键单击 SimpleMathProjection project 节点下的 Dependencies 节点, 选择 Add Project Reference,然后选择 SimpleMathComponent project >OK。
不要尝试构建项目。 我们将在后面的步骤中执行此操作。
到目前为止,解决方案资源管理器应如下所示(版本号会有所不同)。
显示投影项目依赖项
从源代码构建项目
对于
尽管此项已经为 CppWinRTComponentProjectionSample 解决方案配置好了,但是请按照以下步骤,进行自我练习,尝试亲自完成配置。
若要配置解决方案以从源生成:
在 CppWinRTComponentProjectionSample 解决方案仍然打开的情况下,右键单击解决方案节点,然后选择“添加>新项”。 选择 XML 文件 项,并将其命名 Directory.Build.props(没有
.xml扩展名)。 请单击 “是”以覆盖现有文件。将 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>保存并关闭 Directory.Build.props 文件。
编辑项目文件以执行 C#/WinRT
必须先编辑项目文件以指定几个项目属性,然后才能调用 cswinrt.exe 工具生成投影程序集。
在 解决方案资源管理器 中,双击 SimpleMathProjection 节点在编辑器中打开项目文件。
更新
TargetFramework元素以面向特定Windows SDK 版本。 这将添加互操作和投影支持所必需的程序集依赖项。 此示例面向 Windows SDK 版本 net6.0-windows10.0.19041.0(也称为版本 2004 Windows 10)。 将Platform元素设置为 AnyCPU,以便生成的投影程序集可以从任何应用体系结构中进行引用。 若要允许引用应用程序支持早期Windows SDK 版本,还可以设置TargetPlatformMinimumVersion属性。<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>注释
对于本演练和相关示例代码,该解决方案是为 x64 和 Release构建的。 请注意,SimpleMathProjection 项目配置为为所有解决方案体系结构配置生成 AnyCPU。
添加第二个
PropertyGroup元素(紧接在第一个元素之后),该元素设置多个 C#/WinRT 属性。<PropertyGroup> <CsWinRTIncludes>SimpleMathComponent</CsWinRTIncludes> <CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir> </PropertyGroup>下面是有关此示例中设置的一些详细信息:
-
CsWinRTIncludes属性指定要投影的命名空间。 -
CsWinRTGeneratedFilesDir属性设置生成投影源文件的输出目录。 此属性设置为OutDir,在上一节 Directory.Build.props 中定义。
-
保存并关闭 SimpleMathProjection.csproj 文件,然后单击 根据需要 重新加载项目。
使用投影创建 NuGet 包
若要为.NET应用程序开发人员分发投影程序集,可以通过添加一些其他项目属性在生成解决方案时自动创建 NuGet 包。 对于.NET目标,NuGet 包需要包含组件中的投影程序集和实现程序集。
使用以下步骤将 NuGet 规范(
.nuspec)文件添加到 SimpleMathProjection 项目。- 在 解决方案资源管理器 中,右键单击 SimpleMathProjection 节点, 选择 Add>New Folder,并将文件夹命名为nuget。
- 右键单击 nuget 文件夹,选择 添加>新项,选择 XML 文件,并将其命名 为 SimpleMathProjection.nuspec。
在 解决方案资源管理器 中,双击 SimpleMathProjection 节点在编辑器中打开项目文件。 将以下属性组添加到已打开的 SimpleMathProjection.csproj(紧接在两个现有
PropertyGroup元素之后),以自动生成软件包。 这些属性指定要生成 NuGet 包的NuspecFile和目录。<PropertyGroup> <GeneratedNugetDir>.\nuget\</GeneratedNugetDir> <NuspecFile>$(GeneratedNugetDir)SimpleMathProjection.nuspec</NuspecFile> <OutputPath>$(GeneratedNugetDir)</OutputPath> <GeneratePackageOnBuild>true</GeneratePackageOnBuild> </PropertyGroup>注释
如果希望单独生成包,还可以选择从命令行运行
nuget.exe工具。 有关创建 NuGet 包的详细信息,请参阅 使用 nuget.exe CLI创建包。打开 SimpleMathProjection.nuspec 文件以编辑包创建属性,并粘贴以下代码。 以下代码片段是一个 NuGet 规范示例,用于将 simpleMathComponent 分发到多个目标框架。 请注意,为目标 指定投影程序集(SimpleMathProjection.dll)而不是
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 都是体系结构中立的。
保存并关闭刚刚编辑的文件。
生成用于生成投影和 NuGet 包的解决方案
在生成解决方案之前,请确保在 Visual Studio 中的Configuration Manager设置下检查BuildConfiguration Manager。 在本演练中,请将 配置 设置为 发布,并将 平台 设置为 x64 解决方案。
此时,可以生成解决方案。 右键单击解决方案节点,然后选择 生成解决方案。 这将首先生成 SimpleMathComponent 项目,然后生成 SimpleMathProjection 项目。 组件 WinMD 和实现程序集(SimpleMathComponent.winmd 和 SimpleMathComponent.dll)、投影源文件和投影程序集(SimpleMathProjection.dll)都将在 _build 输出目录下生成。 还可以在 \SimpleMathProjection\nuget 文件夹下看到生成的 NuGet 包,SimpleMathComponent0.1.0-prerelease.nupkg。
重要
如果未生成上述任何文件,则再次生成解决方案。 在重新构建之前,你可能还需要关闭并重新打开解决方案。
可能需要关闭并重新打开解决方案,以便.nupkg显示在Visual Studio中,如图所示(或者只需选择并取消选择Show All Files)。
显示投影生成
在 C# .NET 6 控制台应用程序中引用 NuGet 包
若要从.NET项目中使用 SimpleMathComponent,只需将引用 SimpleMathComponent0.1.0-prerelease.nupkg NuGet 包添加到新的.NET项目中即可。 以下步骤演示如何通过在单独的解决方案中创建简单的控制台应用来执行此操作。
使用以下步骤创建包含 C# 控制台应用 项目的新解决方案(在新解决方案中创建此项目可让你独立还原 SimpleMathComponent NuGet 包)。
重要
我们将在 文件夹中创建新的
\CsWinRT\src\Samples\NetProjectionSample项目,你将在下载或克隆 C#/WinRT 投影示例中找到该项目。- 在Visual Studio的新实例中,选择 File>New>Project。
- 在 “创建新项目”对话框中,搜索 控制台应用 项目模板。 选择名为 控制台应用(无前缀或后缀)的 C# 项目模板,然后单击 下一步。 如果使用 Visual Studio 2019,则项目模板Console Application。
- 将新项目命名 SampleConsoleApp,将其位置设置为
\CsWinRT\src\Samples\NetProjectionSample和 SimpleMathProjection 文件夹中所在的同一 文件夹,然后单击 “下一步”。 - 在 Additional information 页上,选择 .NET 8.0 (长期支持),然后选择 Create。
在 解决方案资源管理器 中, 双击 SampleConsoleApp 节点打开 SampleConsoleApp.csproj 项目文件,并编辑
TargetFramework和Platform属性,使其如以下列表所示。 添加Platform元素(如果不存在)。<PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0-windows10.0.19041.0</TargetFramework> <Platform>x64</Platform> </PropertyGroup>在保持 SampleConsoleApp.csproj 项目文件依然打开的情况下,接下来,我们将在 SampleConsoleApp 项目中添加对 SimpleMathComponent NuGet 包的引用。 若要在生成项目时还原 SimpleMathComponent NuGet,可以将
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包的 路径设置为../SimpleMathProjection/nuget。 该路径是正确的,前提是您按照本演练中的步骤进行操作,使得 SimpleMathComponent 和 SampleConsoleApp 项目在同一文件夹中(在本例中为NetProjectionSample文件夹)。 如果执行了其他操作,则需要相应地调整该路径。 或者,可以 向解决方案添加本地 NuGet 包源。编辑 Program.cs 文件以使用 SimpleMathComponent提供的功能。
var x = new SimpleMathComponent.SimpleMath(); Console.WriteLine("Adding 5.5 + 6.5 ..."); Console.WriteLine(x.add(5.5, 6.5).ToString());保存并关闭刚刚编辑的文件,并生成并运行控制台应用。 你将看到以下输出。
已知问题
- 生成投影项目时,可能会看到如下错误:Error MSB3271 在生成的项目“MSIL”和实现文件“..\SimpleMathComponent.winmd”的处理器体系结构"x86"之间存在不匹配。这种不匹配可能会导致运行时失败。请考虑通过 Configuration Manager 更改项目的目标处理器体系结构,以便使项目与实现文件之间的处理器体系结构保持一致,或者选择具有与项目的目标处理器体系结构一致的实现文件的 winmd 文件。 若要解决此错误,请将以下属性添加到 C# 库项目文件:
<PropertyGroup> <!-- Workaround for MSB3271 error on processor architecture mismatch --> <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch> </PropertyGroup>
进一步注意事项
我们演示了如何在本主题中创建的 C# 投影(或互操作)程序集非常简单,它不依赖于其他组件。 但是,若要为具有对Windows 应用 SDK类型的引用的 C++/WinRT 组件生成 C# 投影,需要在投影项目中添加对 Windows 应用 SDK NuGet 包的引用。 如果缺少任何此类引用,则会看到“找不到类型 <T>”等错误。
在这个主题中,我们执行的另一项工作是将投影作为 NuGet 包进行分发。 目前是 必要的。
资源
- 本演练的完整代码示例
- C#/WinRT NuGet 文档
- NuGet.exe 文档