使用 WinForms 构建原生 Windows on Arm 应用





5.00/5 (3投票s)
本文将介绍如何创建一个简单但完整的 WoA 原生 Windows Forms 应用程序。
Windows on Arm (WoA) 设备,例如 Surface Pro X,可以通过模拟运行 x86 Windows 应用。通过 Insider 构建版本,WoA 设备还可以模拟 x64 Windows 应用。
模拟是将 x86 CPU 指令转换为 Arm 指令。当然,这不像原生运行应用程序那样快——也就是说,直接为 Arm 架构和指令集编译。因此,如果您想充分利用平台的强大功能并最大限度地延长电池续航时间,则应创建原生应用。
您可以通过多种方法为 WoA 创建原生应用。例如,您可以创建一个以 Arm 设备为目标的通用 Windows 平台 (UWP) 应用。自从 .NET Framework 5.0.8 版本发布以来,Windows Forms、Windows Presentation Foundation (WPF) 和 UWP 都原生支持 Arm。如果您将这些框架用于现有应用程序,那么将其原生编译为 WoA 设备非常简单。
在本文中,我们将把一个在 x86-64 计算机上开发的 Windows Forms (WinForms) 应用程序移植到一个原生的 WoA WinForms 应用。要跟随学习,您应该熟悉 C#、.NET 和 WinForms。
创建新的 AArch64 项目
作为演示,让我们从创建一个新的 Windows Forms 项目开始,然后将其编译为 AArch64(64 位 Arm)。首先,请确保您已安装 Visual Studio 2019,并勾选了.NET 桌面开发组件。在 Visual Studio 安装程序中,它看起来是这样的:
接下来,我们打开 Visual Studio 并选择创建新项目。在模板列表中,我们选择Windows Forms 应用,*而不是* Windows Forms App (.NET Framework)。我们不选择 Windows Forms App,因为它适用于 .NET Framework 4.x,而我们要的是 .NET 5。
然后,我们点击下一步并为我们的应用程序选择一个项目名称和位置。
之后,向导会询问我们要以哪个框架为目标:.NET Core 3.1 还是 .NET 5。虽然我们可以在以 .NET Core 3.1 为目标时编译到 AArch64,但以 .NET 5 为目标时性能更好,因为它更多地使用了 A 硬件内在函数。所以,我们选择.NET 5.0 并点击创建。然后我们可以像往常一样开始开发 Windows Forms 应用程序。
作为简单的演示,让我们创建一个只包含一个按钮的窗体。当用户单击它时,将弹出一个消息框,显示进程架构。
我们首先在设计器中向窗体添加一个按钮。
然后,我们双击按钮以添加单击事件处理程序。我们使用以下代码作为事件处理程序:
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture.ToString());
}
如果您在开发机上运行应用程序并按下按钮,它可能会显示 x64(当然,除非您不在 x64 设备上)。
编译原生 WoA 应用程序
现在,让我们将应用程序原生编译为 WoA 设备。首先,我们在解决方案资源管理器中双击属性。
这将打开项目的属性。
然后我们转到生成选项卡。
然后,我们只需要将平台目标更改为ARM64 并重新编译。现在我们无法在开发机上运行该应用程序,因为架构不兼容,所以让我们尝试在 Arm 设备上运行它。我们将 `bin/Debug/net5.0-windows` 目录复制到我们的 Arm 设备,然后尝试运行该目录中的 `WinFormsArm.exe`。
如果您尚未安装 .NET 运行时,设备会提示您先安装,然后将您重定向到下载页面。在该页面上,您选择运行控制台应用下的下载 Arm64,运行安装程序,然后对运行桌面应用执行相同的操作(您需要两者)。
当 .NET 运行时安装完成后,我们再次尝试运行应用程序并按下按钮。现在消息框中会显示Arm64。
作为比较,您也可以尝试将平台目标设置为x86,在您的 Arm 设备上下载 x86 运行时,然后运行 x86 可执行文件。您会看到消息框显示“x86”,这意味着它是在模拟下运行的。
如您所见,将 Windows Forms 应用程序编译为 AArch64 非常简单:您只需要确保以.NET 5 为目标并将平台目标更改为ARM64。对于 .NET Framework 4.x 的现有应用程序,您应该在 Visual Studio 中创建一个新项目(因为 .NET 5 和 .NET Framework 4.x 的项目类型不同),然后从旧项目中导入文件。
在为 Windows on Arm 创建或移植应用程序时,请记住缩放和布局的 Windows 设置。像 Surface Pro 这样的设备在相对较小的屏幕上具有高分辨率,因此 200% 是推荐的设置。另一方面,您的开发机可能将此值设置在 100% 到 150% 之间。
缩放和布局设置会影响例如文本和窗体的大小。但是像 PictureBox 这样的控件不会缩放。您需要确保您的应用程序无论此设置的值如何都能正常显示。否则,您的窗体在 Arm 设备上可能会显得相当扭曲。当然,这个问题并非 Arm 所特有,因为所有 Windows 设备都有此设置,但记住这一点很重要。
AArch64 与 x86 模拟的基准测试
与模拟 x86 二进制文件相比,为 AArch64 原生编译的优势在于它更快。让我们做一个快速的基准测试来确认这一点。
我们的基准测试包括生成一个 400 x 400 的 Bitmap,用随机像素填充它,并显示结果。我们预计这在编译为 AArch64 时会比编译为 x86 快。
我们将扩展我们的窗体,添加一个新的按钮来启动基准测试,一个标签来显示花费的时间,以及一个 `PictureBox` 来显示图像。
首先,我们为按钮命名为 `benchmarkBtn`,为标签命名为 `benchmarkLabel`,为 `PictureBox` 命名为 `benchmarkPbox`。然后,我们双击 `benchmarkBtn` 以添加一个执行基准测试的单击事件处理程序:
private void benchmarkBtn_Click(object sender, EventArgs e)
{
Stopwatch sw = new Stopwatch();
sw.Start();
Random rng = new Random();
Bitmap bmp = new Bitmap(400, 400);
for (int i = 0; i < 10; i++)
{
for (int x = 0; x < 400; x++)
{
for (int y = 0; y < 400; y++)
{
bmp.SetPixel(x, y, Color.FromArgb(rng.Next()));
}
}
}
benchmarkPbox.Image = bmp;
sw.Stop();
benchmarkLabel.Text = "Benchmark time: " + sw.Elapsed.ToString();
}
我们需要在文件顶部添加 `using System.Diagnostics;`,因为我们的代码使用了 Stopwatch。
基准测试使用 `SetPixel` 一次填充一个像素,这是一种生成随机图像的低效方法。但是,对于此类基准测试来说,这并不重要,因为结果仅与彼此相关。
生成随机图像的整个过程重复十次,以平均每次迭代的结果。只有最后一张图像会显示,但这也不重要。当图片生成完成后,我们的应用程序会在标签上显示经过的时间。
通过将应用程序编译为 AArch64 和 x86 来进行尝试,然后在您的 Arm 设备上同时运行它们。您还可能需要将生成配置设置为Release 而不是Debug 来优化代码。然后可执行文件会出现在 `bin/Release` 而不是 `bin/Debug` 中。
在我的 Arm 设备上,原生 AArch64 应用程序在 0.6 到 0.7 秒之间完成了基准测试。模拟的 x86 应用程序花费了 1.2 到 1.5 秒。因此,原生应用程序执行此基准测试的速度快了两倍。当然,这测量的是一种特定情况,并非所有操作都有相同的相对速度,但这确实突显了原生编译的应用程序更有效率,正如我们预期的那样。
后续步骤
为了充分利用 WoA 平台,您应该为 Arm 架构原生创建应用程序。对于 Windows Forms 应用程序,可以轻松地以 .NET 5 为目标,并指示 Visual Studio 编译为 AArch64。
要了解有关此主题的更多信息,您可以探索在 Visual Studio 中远程调试 C# 或 Visual Basic 项目。现在您知道如何为 WoA 编写原生 Windows Forms 应用程序了,开始构建您自己的Arm 原生 WinForms 应用吧!