65.9K
CodeProject 正在变化。 阅读更多。
Home

将 WPF 和 .NET 6 的 Windows 应用迁移到 Windows on Arm

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2021 年 7 月 2 日

CPOL

9分钟阅读

viewsIcon

8529

在本文中,我们将了解如何将 Windows .NET 6 WPF 应用程序转换为 Windows on Arm (WoA) 应用程序。

将 Windows .NET 6 WPF 应用程序转换为 Windows on Arm (WoA) 应用程序需要设置开发环境并对应用程序进行一些更改,使其可以在 WoA 上运行,而无需模拟器。

本文是《Windows 应用迁移到 WoA 的今日最佳实践》的后续。

必备组件

在本文中,我们将使用 C# 作为编程语言,将一个 WPF 示例应用程序移植到 WoA 环境,因此需要一些 C# 和 WPF/XAML 知识。

我们将使用 Visual Studio 2019 作为 IDE。您应该知道如何创建项目、类和 WPF 窗体。

引言

上一篇文章中,我们看到了 WoA 如何执行几乎所有 .NET 应用程序。它使用 x86 模拟层来执行未为 Aarch64 处理器编译的程序。尽管模拟层的性能已尽可能优化,但我们注意到,为 WoA 本地编译的应用程序速度可以提高 11 倍。

Microsoft 曾试图将 WPF 整合到 .NET 5 for Arm 框架中,但未能及时完成,因此我们创建了一个 UWP 应用程序,该应用程序可以在 WoA 上以原生模式运行。

在 .NET 6 中,将包含 Arm 版 WPF,所以让我们看看如何使用它。

注意:我们将使用撰写本文时可用的最新测试版本,因此发布版本中可能会有一些更改。

测试应用程序

我们将使用一个测试应用程序来处理一个众所周知的问题:旅行商问题 (TSP)。

该应用程序的主要目的是能够对模拟版本和 Aarch64 上的原生 .NET 6 WPF 进行速度基准测试。因此,我们需要一些 CPU 密集型的、最好是图形化的内容来测试 .NET 6 中 WPF 的功能。

以蛮力方式解决 TSP 需要计算所有点之间的所有可能路径(组合)。大约 12 个点仍然可行,但之后会变得非常慢。

计算 N 个点之间的所有可能路径需要计算 N! 条路径。例如,找到 20 个点之间的最佳路径将需要 20! (2,432,902,008,176,640,000) 次计算。即使在非常快的计算机上,我们也活不到看到结果。

显然,我们需要另一种解决方案。有几种可能性,但我们将实现一种遗传算法 (GA) 来找到该问题的最佳局部解决方案。实际实现超出了本文的范围。我实现的大部分内容在《旅行商 - 遗传算法》中进行了描述。您可以在GVerelst/TravelingSalesman: 使用遗传算法查找最佳路径中找到该应用程序的源代码。

这些是关于该应用程序的重要要点

  • “生成路径”按钮会生成一条新的随机路径。默认情况下,路径将包含 20 个点。
  • 种群大小决定了遗传算法的种群长度。默认设置为 5,000。这意味着每次迭代
    1. 将计算 5,000 条路径的距离。
    2. 将 5,000 条路径按长度排序。
    3. 通过使用交叉和变异算法生成 5,000 条新路径。
    4. 最佳路径显示在画布上。
  • 随机种子用于伪随机生成器。从相同的数字开始将始终生成相同的数字序列。这样我们可以更好地比较算法完成所需的时间。

此版本完全没有错误处理。在任何字段中输入非数字内容或在未先生成路径的情况下开始计算都会导致程序崩溃。如果您想验证数字输入,请查看以下 Stack Overflow 文章:C# WPF 如何在文本框中使用纯数字值 - Stack Overflow

这是算法在进行 1,000,000 次计算后的结果

这不能保证是最佳可能路径,但它已经过大量优化,很可能是一个可接受的解决方案。

初始应用程序是为 .NET 5 编译的,因此它无法在 Arm 设备上运行。

使应用程序在 Aarch64 设备上运行

对我们来说幸运的是,Microsoft 现在正在 .NET 6 中实现 WPF。撰写本文时,已提供 6.0.0-preview.3 版本。

直接在 Windows on Arm 上安装 Visual Studio 2019 尚不支持,并且使用现有的 X64 开发机器可以实现更快的开发 | 部署 | 调试周期。

可以在 Windows on Arm 上运行 Visual Studio,但它将以 x86 模拟模式运行。这太慢了,无法舒适地工作,所以我们使用 x64 Windows 机器。

在我的情况下,我使用的是 Microsoft Azure 中的 B4ms 虚拟机,具有 4 个 CPU 核心、16GB RAM、8 个最大附加磁盘(远超我们需要),以及 2880 IOPS 的磁盘吞吐量以确保良好的性能。

要准备开发机器,首先将 Visual Studio 更新到最新版本(16.11 或更高版本)。最简单的方法是使用 Visual Studio Installer。然后安装 .NET 6 SDK

如果您想在 Visual Studio 2019 中使用预览功能,您需要明确声明。在“选项”对话框(工具 > 选项)中,在搜索字段中输入“Preview”,然后在左侧树中选择预览功能。搜索“使用 .NET Core SDK 的预览版”并选中复选框。需要重启 Visual Studio。

当您重新打开 Visual Studio 时,您会在目标框架的下拉列表中找到 .NET 6.0。

将 WPF 和 GA 项目的目标框架设置为 .NET 6.0,还可以选择性地在测试项目中设置。现在我们准备重新编译应用程序。

结果

这是一个使用 Model-View-ViewMode (MVVM) 模式的 WPF 应用程序,包含一些图形(绘制路径)和一个后台工作线程。因此,它不是最基本的 WPF 应用程序。但是,构建没有任何错误或警告。

它运行正常,找到结果路径所需的时间与 .NET 5 版本几乎相同。这对于框架的最终版本来说很有希望!

Aarch64-原生 .NET

在我们能够生成和运行应用程序的 Aarch64 原生版本之前,我们需要先在 Arm 设备上安装 .NET 6 预览版,您可以在此处下载。安装后甚至不需要重启设备。

请注意,如果您已经安装了 x86 .NET Core 或 .NET 5 SDK,64 位 Arm .NET SDK 不会覆盖其他 SDK 的 PATH 变量 - 至少在最新的 .NET 6 预览版中是这样。这可能会在 .NET 6 发布周期晚些时候发生变化。安装 .NET 6 预览版后,请在终端中运行 dotnet --info 进行检查。您将看到类似以下内容

PS C:\> dotnet --info
.NET SDK (reflecting any global.json):
 Version:   5.0.100
 Commit:    5044b93829

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.19042
 OS Platform: Windows
 RID:         win10-x86
 Base Path:   C:\Program Files (x86)\dotnet\sdk\5.0.100\

检查 RID 字段。如果显示 win10-arm64,则表示一切正常,您可以跳到下面的编译为 Aarch64 部分。否则,您需要更新系统路径以包含 arm64 原生 .NET SDK。为此,请打开 Windows 设置应用,然后搜索“环境变量”。

通过双击变量来验证用户变量中的 Path 变量。

确保您有一个条目显示 C:\Program Files\dotnet,并且如果您看到一个条目显示 C:\Program Files(x86)\dotnet,请将其删除。现在,如果您运行 dotnet –info,您应该会看到

PS C:\Users\gvere> dotnet --info
.NET SDK (reflecting any global.json):
 Version:   6.0.100-preview.3.21167.6
 Commit:    fa9dcf862f

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.19042
 OS Platform: Windows
 RID:         win10-arm64
 Base Path:   C:\Program Files\dotnet\sdk\6.0.100-preview.3.21167.6\

Host (useful for support):
  Version: 6.0.0-preview.3.21167.1
  Commit:  0f64b267ac

这可能只是我的设备上的问题,但如果您遇到同样的问题,您现在就知道要查找什么了!我不是唯一遇到此问题的人,如果您想了解更多信息,请查看:Windows 找不到最新安装的 .NET SDK - Stack Overflow。

编译为 Aarch64

以下是让一切正常工作的最后几个步骤。

在开发机器上打开 TSP.WPF 项目,并按如下方式添加 <RuntimeIdentifiers> 元素。

TSP.WPF.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net6.0-windows</TargetFramework>
    <RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
    <UseWPF>true</UseWPF>
    <Authors>Gaston Verelst</Authors>
    <Company>Faq.be bvba</Company>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\TSP.GA\TSP.GA.csproj" />
  </ItemGroup>
</Project>

接下来,我们需要创建一个发布配置文件。

  1. 右键单击 TSP.WPF 项目,然后选择发布…
  2. 对于目标,选择文件夹,然后单击下一步

  3. 对于具体目标,也选择文件夹,然后单击下一步

  4. 选择二进制文件发布的位置。通常默认设置即可。如果您使用共享文件夹部署到设备,可以在此处选择该文件夹,这样就可以跳过文件复制。
  5. 单击完成以创建初始发布配置文件。
  6. 更多选项下,单击编辑。将值设置为如下所示,然后单击保存

  7. 单击发布

Visual Studio 现在将编译我们项目的一个版本,该版本可以在 Aarch64 设备上原生执行。

将此文件夹复制到您的设备并运行应用程序。现在它正在 Arm 设备上以 64 位原生模式运行,在 .NET 6 上使用 WPF!

结论

找到与之前相同的路径所需的时间现在是 1 分钟 23 秒,这与开发 VM 相当。我们可以再次说在 Aarch64 设备上运行 .NET 6 性能相当不错。

为 WPF on .NET 6 构建项目需要一些额外的步骤,但不需要更改代码。

在 64 位 Windows on Arm 机器上安装 .NET 6 除了 PATH 问题外都很简单。如果您同时运行 win10-x86 和 win10-arm64 .NET SDK,请务必检查这一点。

原生运行 WPF 应用程序只需要对 .csproj 文件进行少量更改,并使用正确的 Target runtime 进行发布。完成这些后,应用程序就可以无缝运行了!

后续步骤

尽管 .NET 6 仍处于预览阶段,但 WPF 支持已经运行良好。如我们所见,将现有的 x86/x64 WPF 应用程序迁移到 Aarch64 Windows 机器上原生运行相对简单。

如果您的 WPF 应用程序通过 PInvoke 调用本地 C/C++ 库或使用包装 C/C++ 库的 NuGet 包,您需要格外小心。如果这样做,您将需要这些库的 Aarch64 版本才能原生运行您的应用程序。

如果您遇到问题,此线程是一个很好的起点。

有关 .NET 6 的更多信息,请参阅《Announcing .NET 6 Preview 3 | .NET Blog (microsoft.com)》

© . All rights reserved.