从 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 文件。
- C++/CLI 对 .NET 的支持仅适用于 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 样式的项目文件。
- 将
<CLRSupport>true</CLRSupport>
属性替换为<CLRSupport>NetCore</CLRSupport>
属性。 此属性通常位于特定于配置的属性组中,因此可能需要将其替换在多个位置。 - 将
<TargetFrameworkVersion>
属性替换为<TargetFramework>net8.0</TargetFramework>
属性。 请务必更改标记和值。 - 删除对
System
、System.Data
、System.Windows.Forms
和System.Xml
,如<Reference Include="System" />
.NET Framework 的任何引用。 使用<CLRSupport>NetCore</CLRSupport>
时会自动引用 .NET SDK 程序集。 - 根据需要更新 .cpp 文件中的 API 使用,以移除对 .NET 不可用的 API。 由于C++/CLI 项目往往相当精简的互作层,因此通常不需要进行很多更改。 可以使用 .NET 可移植性分析器 来识别C++/CLI 二进制文件使用的不支持的 .NET API。
- 如果项目是可执行文件,请执行以下步骤:
- 将项目类型更改为库。
- 创建新的 .NET 可执行项目。
- 在 .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 文件:
<!-- Reference all of Windows Forms -->
<FrameworkReference Include="Microsoft.WindowsDesktop.App.WindowsForms" />
若要使用 WPF API,请将此引用添加到 .vcxproj 文件:
<!-- Reference all of WPF -->
<FrameworkReference Include="Microsoft.WindowsDesktop.App.WPF" />
若要同时使用 Windows 窗体和 WPF API,请将此引用添加到 .vcxproj 文件:
<!-- 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 项目:
编译时,将
-clr:netcore
传递给 cl.exe。引用必要的 .NET 引用程序集。
链接时,请提供 .NET 应用程序主机目录作为
LibPath
,以便找到 ijwhost.lib。将ijwhost.dll 从 .NET 应用主机目录复制到项目的输出目录。
确保运行托管代码的应用程序的第一个组件存在 runtimeconfig.json 文件。 对于最新版本的 Visual Studio,会自动创建并复制 runtime.config 文件。
对于较旧版本的 Visual Studio,如果应用程序具有本机入口点,则需要手动创建以下 runtimeconfig.json 文件,以便第一个C++/CLI 库使用 .NET 运行时。 如果从托管入口点调用C++/CLI 库,则库不需要 runtimeconfig.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 问题。