使用英语阅读

通过


如何将 C++/CLI 项目移植到 .NET

从 Visual Studio 2019 开始,C++/CLI 项目可以面向 .NET。 有了这项支持,就可以将具有 C++/CLI 互操作层的 Windows 桌面应用程序从 .NET Framework 移植到 .NET。 本文介绍如何将 C++/CLI 项目从 .NET Framework 移植到 .NET。

C++/CLI .NET Core 限制

与 .NET Framework 相比,C++/CLI 项目和 .NET 存在一些重要限制:

  • 不支持将 C++/CLI 项目编译为可执行文件。 必须编译为 DLL。
  • .NET 的 C++/CLI 支持仅适用于 Windows。
  • C++/CLI 项目不能面向 .NET Standard。
  • C++/CLI 项目不支持较新的 SDK 样式项目文件格式。 相反,C++/CLI 项目使用其他 Visual Studio C++ 项目使用的 .vcxproj 文件格式
  • C++/CLI 项目不能同时面向多个 .NET 平台。 如果需要同时为 .NET 和 .NET Framework 生成 C++/CLI 项目,请使用单独的项目文件。
  • .NET 不支持 -clr:pure-clr:safe 编译,仅支持较新的 -clr:netcore 选项(等效于 .NET Framework 的 -clr)。

移植 C++/CLI 项目

若要将 C++/CLI 项目移植到 .NET,请对 vcxproj 文件进行以下更改。 这些迁移步骤不同于其他项目类型所需的步骤,因为 C++/CLI 项目不使用 SDK 样式的项目文件。

  1. <CLRSupport>true</CLRSupport> 属性替换为 <CLRSupport>NetCore</CLRSupport>。 此属性通常位于特定于配置的属性组中,因此你可能需要在多处替换它。
  2. <TargetFrameworkVersion> 属性替换为 <TargetFramework>net8.0</TargetFramework>。 请务必更改标记和值。
  3. 移除对 SystemSystem.DataSystem.Windows.FormsSystem.Xml 的任何 .NET Framework 引用,例如 <Reference Include="System" />。 使用 <CLRSupport>NetCore</CLRSupport> 时,将自动引用 .NET SDK 程序集。
  4. 根据需要更新 cpp 文件中的 API 使用情况,以删除 .NET 不可使用的 API。 由于 C++/CLI 项目通常是非常精简的互操作层,因此通常不需要进行诸多更改。 可以使用 .NET 可移植性分析器来识别 C++/CLI 二进制文件使用的不受支持的 .NET API。
  5. 如果项目是可执行文件,请执行以下步骤:
    1. 将项目类型更改为库。
    2. 创建新的 .NET 可执行项目。
    3. 在 .NET 可执行项目中,添加对 C++/CLI .NET 库的引用。

WPF 和 Windows 窗体使用情况

.NET C++/CLI 项目可以使用 Windows 窗体和 WPF API。 要使用这些 Windows 桌面 API,需要将显式框架引用添加到 UI 库。 使用 Windows 桌面 API 的 SDK 样式项目通过使用 Microsoft.NET.Sdk.WindowsDesktop SDK 来自动引用必要的框架库。 由于 C++/CLI 项目不使用 SDK 样式的项目格式,因此它们在面向 .NET Core 时需要添加显式框架引用。

要使用 Windows 窗体 API,请将此引用添加到 .vcxproj 文件:

XML
<!-- Reference all of Windows Forms -->
<FrameworkReference Include="Microsoft.WindowsDesktop.App.WindowsForms" />

要使用 WPF API,请将此引用添加到 .vcxproj 文件:

XML
<!-- Reference all of WPF -->
<FrameworkReference Include="Microsoft.WindowsDesktop.App.WPF" />

要同时使用 Windows 窗体和 WPF API,请将此引用添加到 .vcxproj 文件:

XML
<!-- Reference the entirety of the Windows desktop framework:
     Windows Forms, WPF, and the types that provide integration between them -->
<FrameworkReference Include="Microsoft.WindowsDesktop.App" />

目前,不能使用 Visual Studio 的引用管理器来添加这些引用。 而是改为通过手动编辑项目文件来进行更新。 在 Visual Studio 中,你需要先卸载项目。 还可以使用其他编辑器,例如 Visual Studio Code。

在不使用 MSBuild 的情况下生成

还可以在不使用 MSBuild 的情况下生成 C++/CLI 项目。 请按照以下步骤,使用 cl.exe 和 link.exe 直接生成适用于 .NET Core 的 C++/CLI 项目

  1. 编译时,将 -clr:netcore 传递给 cl.exe

  2. 引用必要的 .NET 引用程序集。

  3. 链接时,提供 .NET 应用主机目录作为 LibPath,以便可以找到 ijwhost.lib

  4. 将 ijwhost.dll 从 .NET 应用主机目录复制到项目的输出目录

  5. 确保应用程序中运行托管代码的第一个组件存在 runtimeconfig.json 文件。 对于最新版本的 Visual Studio,会自动创建并复制 runtime.config 文件。

    对于较旧版本的 Visual Studio,如果应用程序具有原生入口点,则需要为第一个 C++/CLI 库手动创建以下 runtimeconfig.json 文件以使用 .NET 运行时。 如果是从托管入口点调用某个 C++/CLI 库,则该库不需要 runtimeconfig.json 文件,因为入口点程序集有一个在启动运行时时使用的该文件

    JSON
    {
       "runtimeOptions": {
          "tfm": "net8.0",
          "framework": {
          "name": "Microsoft.NETCore.App",
          "version": "8.0.0"
          }
       }
    }
    

备注

面向 .NET 7 或更高版本的 C++/CLI 程序集始终加载到默认的 AssemblyLoadContext 中。 但是,在 .NET 6 和早期版本中,C++/CLI 程序集可能会加载多次,每次都加载到一个新的 AssemblyLoadContext 中。 如果是首次在某个 C++/CLI 程序集中执行该托管代码:

  • 来自原生调用方,该程序集加载到单独的 AssemblyLoadContext 中。
  • 来自托管调用方,该程序集加载到与调用方相同的 AssemblyLoadContext 中,这通常是默认设置。

若要始终将 C++/CLI 程序集加载到默认的 AssemblyLoadContext 中,你可以将入口点程序集中的“初始化”样式调用添加到 C++/CLI 程序集。 有关详细信息,请参阅此 dotnet/runtime 问题