使用 .NET 升级助手将 Windows 窗体应用升级到 .NET 6

.NET 升级助手是一种命令行工具,可帮助将 .NET Framework Windows 窗体应用升级到 .NET 6。 本文提供以下内容:

  • 演示如何针对 .NET Framework Windows 窗体应用运行该工具
  • 故障排除提示

若要详细了解如何安装此工具,请查看 .NET 升级助手概述

演示应用

可以使用基本 Windows 窗体示例项目来测试通过升级助手进行升级。

分析应用

.NET 升级助手工具包括一种分析模式,可执行升级应用的简化试运行。 它可以提供有关在升级开始之前可能需要进行的更改的见解。 打开终端,导航到目标项目或解决方案所在的文件夹。 运行 upgrade-assistant analyze 命令,传入正在升级的项目或解决方案的名称。

例如,使用基本 Windows 窗体示例应用运行分析模式会产生以下输出,表明在升级之前不会进行任何更改:

> upgrade-assistant analyze .\MatchingGame.sln

16:18:52 INF] Loaded 5 extensions
[16:18:53 INF] Using MSBuild from C:\Program Files\dotnet\sdk\6.0.200-preview.22055.15\
[16:18:53 INF] Using Visual Studio install from C:\Program Files\Microsoft Visual Studio\2022\Preview [v17]
[16:18:55 INF] Recommending Windows TFM net6.0-windows because the project either has Windows-specific dependencies or builds to a WinExe
[16:18:56 INF] Reference to .NET Upgrade Assistant analyzer package (Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers, version 0.3.261602) needs to be added
[16:18:57 INF] Adding Microsoft.Windows.Compatibility 6.0.0
[16:18:57 INF] Reference to .NET Upgrade Assistant analyzer package (Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers, version 0.3.261602) needs to be added
[16:18:57 INF] Running analyzers on MatchingGame
[16:18:59 INF] Identified 0 diagnostics in project MatchingGame
[16:18:59 INF] Running analyzers on MatchingGame.Logic
[16:18:59 INF] Identified 0 diagnostics in project MatchingGame.Logic
[16:18:59 WRN] HighDpiMode needs to set in Main() instead of app.config or app.manifest - Application.SetHighDpiMode(HighDpiMode.<setting>). It is recommended to use SystemAware as the HighDpiMode option for better results.

输出中有很多内部诊断信息,但某些信息非常有用。 请注意,分析模式指示升级将建议项目以 net6.0-windows 目标框架名字对象 (TFM) 为目标。 这是因为解决方案引用的项目是 Windows 窗体项目,是一种仅限 Windows 的技术。 控制台应用程序可能会获得直接升级到 TFM net6.0 的建议,除非它使用某些特定于 Windows 的库。

如果报告了任何错误或警告,请在开始升级前处理这些错误或警告。

运行升级助手

打开终端,导航到目标项目或解决方案所在的文件夹。 运行 upgrade-assistant upgrade 命令,传入正在升级的项目或解决方案的名称。

如果提供了项目,升级过程将立即在该项目上启动。 如果提供了解决方案,你将选择通常运行的项目,称为升级入口点。 根据该项目,将创建一个依赖项关系图,并提供有关升级项目的顺序的建议。

upgrade-assistant upgrade .\MatchingGame.sln

该工具将运行并显示它将执行的步骤列表。 完成每个步骤后,该工具将提供一组命令,让用户应用或跳过下一步或其他选项,例如:

  • 获取有关此步骤的详细信息。
  • 更改项目。
  • 调整日志记录设置。
  • 停止升级并退出。

Enter 而不选择数字会选择列表中的第一项。

每个步骤初始化后,如果应用该步骤,它可能会提供有关其认为会发生的情况的信息。

选择入口点

升级示例应用的第一步是选择解决方案中的哪个项目作为入口点项目。

Upgrade Steps

1. [Next step] Select an entrypoint
2. Select project to upgrade

Choose a command:
   1. Apply next step (Select an entrypoint)
   2. Skip next step (Select an entrypoint)
   3. See more step details
   4. Configure logging
   5. Exit

选择“命令 1”开始该步骤。 结果显示:

[16:32:05 INF] Applying upgrade step Select an entrypoint
Please select the project you run. We will then analyze the dependencies and identify the recommended order to upgrade projects.
   1. MatchingGame
   2. MatchingGame.Logic

列出了两个项目:主 Windows 窗体应用 (MatchingGame) 和库项目 (MatchingGame.Logic)。 为入口点选择 MatchingGame 项目,即“项 1”。

选择要升级的项目

确定入口点后,下一步是选择要首先升级的项目。 在此示例中,该工具确定应先升级库项目 (MatchingGame.Logic),因为主 Windows 窗体应用项目依赖于它。

[16:36:20 INF] Applying upgrade step Select project to upgrade
Here is the recommended order to upgrade. Select enter to follow this list, or input the project you want to start with.
   1. MatchingGame.Logic
   2. MatchingGame

此处建议的路径是先升级 MatchingGame.Logic 项目,因为 MatchingGame 项目依赖于它。 最好遵循建议的升级路径。

升级 MatchingGame.Logic 是很简单的项目,升级后不会出现任何问题。 因此,本文在此时假设该项目已经升级,下一个要升级的项目是 MatchingGame。

升级项目

选择某个项目后,将列出该工具将执行的升级步骤的列表。

重要

就本示例而言,介绍的是 MatchingGame 项目。 假设已成功升级 MatchingGame.Logic 项目。 演示 MatchingGame 项目的原因是,它包含更多在升级应用时会遇到的常见问题。

根据正在升级的项目,你可能会也可能不会看到此示例中列出的每个步骤。

以下输出描述了升级项目所涉及的步骤:

[16:38:44 INF] Initializing upgrade step Back up project

Upgrade Steps

Entrypoint: C:\code\Work\temp\Migration\winforms\net45cs\MatchingGame\MatchingGame.csproj
Current Project: C:\code\Work\temp\Migration\winforms\net45cs\MatchingGame\MatchingGame.csproj

1. [Next step] Back up project
2. Convert project file to SDK style
3. Clean up NuGet package references
4. Update TFM
5. Update NuGet Packages
6. Add template files
7. Update Winforms Project
    a. Default Font API Alert
    b. Winforms Source Updater
8. Upgrade app config files
    a. Convert Application Settings
    b. Convert Connection Strings
    c. Disable unsupported configuration sections
9. Update source code
    a. Apply fix for UA0002: Types should be upgraded
    b. Apply fix for UA0012: 'UnsafeDeserialize()' does not exist
10. Move to next project

Choose a command:
   1. Apply next step (Back up project)
   2. Skip next step (Back up project)
   3. See more step details
   4. Select different project
   5. Configure logging
   6. Exit

备注

本文的其余部分不会明确显示升级步骤,除非有重要的内容需要指出。仍显示每个步骤的结果。

创建备份

在升级项目的这一示例中,你将应用每个步骤。 第一步(命令 1)是备份项目:

[16:43:22 INF] Applying upgrade step Back up project
Please choose a backup path
   1. Use default path [C:\code\Work\temp\Migration\winforms\net45cs.backup]
   2. Enter custom path

该工具选择一个以当前文件夹命名的默认备份路径,但后面追加了 .backup。 可以选择自定义路径作为默认路径的替代。 对于每个升级的项目,会将项目的文件夹复制到备份文件夹。 在此示例中,MatchingGame 文件夹在备份步骤中从 net45cs\MatchingGame 复制到 net45cs.backup\MatchingGame:

[16:43:37 INF] Backing up C:\code\Work\temp\Migration\winforms\net45cs\MatchingGame to C:\code\Work\temp\Migration\winforms\net45cs.backup\MatchingGame
[16:43:37 INF] Project backed up to C:\code\Work\temp\Migration\winforms\net45cs.backup\MatchingGame
[16:43:37 INF] Upgrade step Back up project applied successfully
Please press enter to continue...

升级项目文件

项目将从 .NET Framework 项目格式升级到 .NET SDK 项目格式。

[16:44:31 INF] Applying upgrade step Convert project file to SDK style
[16:44:31 INF] Converting project file format with try-convert, version 0.3.261602+8aa571efd8bac422c95c35df9c7b9567ad534ad0
[16:44:31 INF] Recommending Windows TFM net6.0-windows because the project either has Windows-specific dependencies or builds to a WinExe
C:\code\Work\temp\Migration\winforms\net45cs\MatchingGame\MatchingGame.csproj contains an App.config file. App.config is replaced by appsettings.json in .NET Core. You will need to delete App.config and migrate to appsettings.json if it's applicable to your project.
[16:44:32 INF] Converting project C:\code\Work\temp\Migration\winforms\net45cs\MatchingGame\MatchingGame.csproj to SDK style
[16:44:32 INF] Project file converted successfully! The project may require additional changes to build successfully against the new .NET target.
[16:44:33 INF] Upgrade step Convert project file to SDK style applied successfully
Please press enter to continue...

注意每个步骤的输出。 请注意,示例输出表明你将在升级后完成手动步骤:

App.config 被 .NET Core 中的 appsettings.json 替换。 如果它适用于你的项目,你将需要删除 App.config 并迁移到 appsettings.json。

作为此升级步骤的一部分,packages.config 引用的 NuGet 包将迁移到项目文件。

清理 NuGet 引用

更新项目格式后,下一步是清理 NuGet 包引用。

除了你的应用引用的包,packages.config 文件还包含对这些包的依赖项的引用。 例如,如果添加了对包 A 的引用,而该包依赖于包 B,则这两个包都将在 packages.config 文件中引用。 在新的项目系统中,只需要对包 A 的引用。 此步骤分析包引用并删除不需要的包引用。

[16:46:06 INF] Applying upgrade step Clean up NuGet package references
[16:46:06 INF] Removing outdated package reference: MetroFramework.Design, Version=1.2.0.3
[16:46:06 INF] Removing outdated package reference: MetroFramework.Fonts, Version=1.2.0.3
[16:46:06 INF] Removing outdated package reference: MetroFramework.RunTime, Version=1.2.0.3
[16:46:06 INF] Adding package reference: Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers, Version=0.3.261602
[16:46:08 INF] Upgrade step Clean up NuGet package references applied successfully
Please press enter to continue...

你的应用仍在引用 .NET Framework 程序集。 其中一些程序集可以作为 NuGet 包提供。 此步骤会分析这些程序集并引用适当的 NuGet 包。

处理 TFM

接下来,该工具会将 TFM 从 .NET Framework 更改为建议的 SDK。 在此示例中,它是 net6.0-windows

[16:47:16 INF] Applying upgrade step Update TFM
[16:47:16 INF] Recommending Windows TFM net6.0-windows because the project either has Windows-specific dependencies or builds to a WinExe
[16:47:18 INF] Updated TFM to net6.0-windows
[16:47:18 INF] Upgrade step Update TFM applied successfully
Please press enter to continue...

升级 NuGet 包

接下来,该工具会将项目的 NuGet 包更新到支持更新的 TFM net6.0-windows 的版本。

[16:47:47 INF] Applying upgrade step Update NuGet Packages
[16:47:47 INF] Removing outdated package reference: Microsoft.CSharp, Version=4.7.0
[16:47:47 INF] Removing outdated package reference: System.Data.DataSetExtensions, Version=4.5.0
[16:47:47 INF] Adding package reference: Microsoft.Windows.Compatibility, Version=6.0.0
[16:47:49 INF] Upgrade step Update NuGet Packages applied successfully
Please press enter to continue...

如果工具确定不需要对项目执行任何操作,则工具会自动跳过后续几个步骤。

Windows 窗体项目的特定更新

第一次特定于 Windows Forms 的升级实际上是通知你 Windows 窗体中的默认字体已更改。 默认字体已从“Microsoft Sans Serif 8.25pt (.NET Framework)”更改为“Segoe UI 9pt (.NET 6 或更高版本)”。 有关详细信息,请参阅Windows 窗体中的新增功能:默认字体。 由于此更改,可能需要查看所有窗体和用户控件。 更改默认字体可能会影响窗体上控件的布局。

[16:48:45 INF] Applying upgrade step Default Font API Alert
[16:48:45 WRN] Default font in Windows Forms has been changed from Microsoft Sans Serif to Seg Segoe UI, in order to change the default font use the API - Application.SetDefaultFont(Font font). For more details see here - https://devblogs.microsoft.com/dotnet/whats-new-in-windows-forms-in-net-6-0-preview-5/#application-wide-default-font.
[16:48:45 INF] Upgrade step Default Font API Alert applied successfully
Please press enter to continue...

Windows 窗体接下来的一次更改是更新应用启动逻辑以调用 SetHighDpiMode 方法,该方法可设置 Windows 窗体应用程序的 DPI 模式。 以前此项在 app.config 或 app.config 文件中进行设置。 这也可能会影响窗体上控件的布局。

[16:49:05 INF] Applying upgrade step Winforms Source Updater
[16:49:05 WRN] HighDpiMode needs to set in Main() instead of app.config or app.manifest - Application.SetHighDpiMode(HighDpiMode.<setting>). It is recommended to use SystemAware as the HighDpiMode option for better results.
[16:49:05 INF] Updated Program.cs file at C:\code\Work\temp\Migration\winforms\net45cs\MatchingGame\Program.cs with HighDPISetting set to SystemAware
[16:49:05 INF] Upgrade step Winforms Source Updater applied successfully
[16:49:05 INF] Applying upgrade step Update Winforms Project
[16:49:05 INF] Upgrade step Update Winforms Project applied successfully
Please press enter to continue...

配置和代码文件

接下来,需要迁移 app.config 文件。 没有任何连接字符串或设置需要迁移到新的 appsettings.json 文件。 如果检查示例项目的输出,可发现初始化项显示了三次,每一项都是不同步骤。 如果某个步骤不执行任何操作,则会跳过该步骤,这与步骤“升级应用配置文件”和“更新源代码”相同:

[07:35:56 INF] Initializing upgrade step Upgrade app config files
[07:35:56 INF] Found 0 app settings for upgrade:
[07:35:56 INF] Found 0 connection strings for upgrade:
[07:35:56 INF] Initializing upgrade step Update source code
[07:35:56 INF] Running analyzers on MatchingGame
[07:35:57 INF] Identified 0 diagnostics in project MatchingGame
[07:35:57 INF] Initializing upgrade step Move to next project

完成升级

如果还有更多项目要迁移,该工具可让你选择接下来要升级的项目。 当没有更多项目要升级时,该工具会将你带到“完成升级”步骤:

1. [Next step] Finalize upgrade

Choose a command:
   1. Apply next step (Finalize upgrade)
   2. Skip next step (Finalize upgrade)
   3. See more step details
   4. Configure logging
   5. Exit

升级完成后,迁移的 Windows 窗体项目将类似于以下 XML:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-windows</TargetFramework>
    <OutputType>WinExe</OutputType>
    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
    <UseWindowsForms>true</UseWindowsForms>
    <ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\MatchingGame.Logic\MatchingGame.Logic.csproj" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="MetroFramework" Version="1.2.0.3" />
    <PackageReference Include="Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers" Version="0.3.261602">
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.Windows.Compatibility" Version="6.0.0" />
  </ItemGroup>
</Project>

请注意,.NET 升级助手还会向项目添加分析器,它可帮助继续执行升级过程,例如 Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers NuGet 包。

Visual Basic 项目

如果使用 Visual Basic 对项目进行编码,.NET 升级助手可能包含其他步骤,例如迁移 My 命名空间。 当你的项目使用这些功能时,你应该只会看到添加的这些步骤。 使用示例应用时,MatchingGame.Logic 中的代码使用 My 命名空间来访问注册表。 此项目将包含一个与 My 相关的步骤:

7. Update Visual Basic project
    a. Update vbproj to support "My." namespace

升级后

升级项目后,需要对其进行编译和测试。 在完成升级过程中,你肯定还有更多工作要做。 你的应用的 .NET Framework 版本可能包含项目实际未使用的库引用。 你需要分析每个引用并确定它是否是必需的。 该工具可能还添加或升级了对错误版本的 NuGet 包引用。

本文发布之时,MatchingGame.Logic 项目无法编译。 此项目使用的是 Windows 注册表,它不像 .NET Framework 那样由 .NET 6 直接提供。 若要访问 Windows 注册表,请将 NuGet 包 Microsoft.Win32.Registry 添加到该项目。

<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />

修复该项后,示例应用会进行编译和运行。

故障排除提示

使用 .NET 升级助手时,可能会出现一些已知问题。 某些情况下,.NET 升级助手在内部使用的 try-convert 工具会出现问题。

有关更多故障排除提示和已知问题,可查看此工具的 GitHub 存储库

另请参阅