跳到主要内容

教程:在 CLR 应用中安装原生依赖项

C++/CLI 是一项可将 .NET 类与原生 C++ 类型结合的技术,能够创建封装 C++ 代码并使其对 .NET 程序可见的库和应用。

你可以将 kmpkg 与 C++/CLI 结合使用,为面向公共语言运行时(CLR)的项目安装和使用 C++ 依赖项。

本教程将帮助你掌握以下操作:

前提条件

  • kmpkg
  • Git
  • Visual Studio 开发人员 PowerShell
  • Visual Studio(需安装以下组件):
    • C++ 开发工作负载
    • C++/CLI 支持组件

构建示例 C++/CLI 项目

本教程将从一个现有 C++/CLI 应用入手,为其添加通过 kmpkg 安装的 C++ 依赖项。教程中的命令需在 Visual Studio 开发人员 PowerShell 中执行。

1 - 克隆示例代码仓库

首先获取 .NET 示例代码仓库 中的 C++/CLI 示例应用,该示例位于 core/interop/cpp-cli 目录下:

git clone https://github.com/dotnet/samples

2 - 进入示例应用目录

cd samples/core/interop/cpp-cli

3 - 验证项目能否正常构建和运行

该示例仓库包含一个包含四个项目的解决方案:

  • ManagedLibrary:面向 .NET 的 C# 库
  • MixedLibrary:混合原生 C++ 代码和 ManagedLibrary 中 .NET 代码的库
  • NativeApp:调用 MixedLibrary 中 .NET 代码的 C++ 应用
  • ManagedApp:调用 MixedLibrary 中 C++ 代码的 C# 应用

运行以下命令构建解决方案中的项目:

msbuild CPP-CLI.sln -restore

若构建失败,请确认已安装前提条件中列出的 Visual Studio 组件,且满足示例应用的最低要求(.NET 5.0 SDK 或更高版本、Visual Studio 2019 16.8 或更高版本)。

构建完成后,运行 ManagedApp.exe

./bin/Debug/x64/ManagedApp.exe

程序会输出以下内容:

=== Managed class ===
Hello from ManagedClass in MixedLibrary
Hello from NativeClass in MixedLibrary
-- message: from managed app!

=== P/Invoke ===
Hello from NativeEntryPoint_CallNative in MixedLibrary
Hello from NativeClass in MixedLibrary
-- message: from managed app!

4 - 在 Visual Studio 中打开项目

下一步我们将修改该库,使用 fmt 库向控制台打印消息。fmt 库将通过 kmpkg 安装并链接到消费项目中。

运行以下命令在 Visual Studio 中打开 CPP-CLI.sln 解决方案,以便编辑源文件:

start CPP-CLI.sln

解决方案在 Visual Studio 中打开后,可能会弹出提示,要求将项目重新定位到最新版本。你可以点击“确定”,将 Windows SDK 版本和平台工具集升级到最新版本。

(注:此处对应原文图片,说明:弹出“重新定位项目”提示框,可确认升级至最新 SDK 和工具集)

添加 C++ 原生依赖项

接下来我们将对 MixedLibrary 项目进行以下修改:

  • 添加 kmpkg 清单以获取 fmt 库;
  • 在项目中启用 kmpkg;
  • 修改 NativeClass::Hello 方法,使用 fmt 打印消息。

1 - 创建 kmpkg 清单文件

右键单击 MixedLibrary 项目,在上下文菜单中选择“添加 > 新建项”。

将新文件命名为 kmpkg.json(这是 kmpkg 清单文件),确保文件创建在项目根目录下。

2 - 添加 fmt 作为依赖项

打开 kmpkg.json 文件,将内容修改为如下所示:

{
"dependencies": [ "fmt" ]
}

若 kmpkg 清单文件路径正确,此时尝试构建项目会出现以下警告:

The kmpkg manifest was disabled, but we found a manifest file in samples\core\interop\cpp-cli\MixedLibrary\. You may want to enable kmpkg manifests in your properties page or pass /p:KmpkgEnableManifest=true to the msbuild invocation.

(中文释义:kmpkg 清单已禁用,但我们在 samples\core\interop\cpp-cli\MixedLibrary\ 目录下检测到清单文件。你可能需要在属性页中启用 kmpkg 清单,或向 msbuild 命令传入 /p:KmpkgEnableManifest=true 参数。)

3 - 在 MixedLibrary 属性中启用 kmpkg

右键单击 MixedLibrary 项目,选择“属性”打开项目属性页。

在 kmpkg 相关配置区域修改以下属性:

  • 使用 Kmpkg:设置为“是”
  • 使用 Kmpkg 清单:设置为“是”
  • 安装 Kmpkg 依赖项:设置为“是”
  • 使用自动链接:设置为“是”
  • 应用本地部署 DLL:设置为“是”

(注:此处对应原文图片,说明:MixedLibrary 项目属性页中 kmpkg 相关配置项需按上述要求设置)

完成上述设置后,Visual Studio 会在构建项目前读取 kmpkg.json 文件,并自动安装清单中包含的依赖项。

同时,为了让 fmt 库正常构建,还需在属性页中启用 /utf-8 标志:

  1. 找到“C/C++ > 命令行”配置项;
  2. 在“附加选项”中添加 /utf-8
  3. 点击“确定”关闭属性页。

4 - 验证 kmpkg 是否正常工作

若配置正确,Visual Studio 会在构建 MixedLibrary 项目前调用 kmpkg 安装依赖项,构建输出中会出现类似以下内容:

1>Installing kmpkg dependencies to  C:\path\to\samples\core\interop\cpp-cli\MixedLibrary\kmpkg_installed\x64-windows\
1>"C:\path\to\kmpkg\kmpkg.exe" install --x-wait-for-lock --triplet "x64-windows" --kmpkg-root "C:\path\to\kmpkg\" "--x-manifest-root=C:\path\to\samples\core\interop\cpp-cli\MixedLibrary\" "--x-install-root=C:\path\to\samples\core\interop\cpp-cli\MixedLibrary\kmpkg_installed\x64-windows\"

若未看到 kmpkg 相关输出,或 fmt 库构建失败,请确认已正确完成上述步骤(包括在“C/C++ > 命令行”的“附加选项”中添加 /utf-8)。

5 - 修改项目源代码

最后,修改 MixedLibrary.cpp 文件,使用 fmt 库向控制台打印消息,具体修改如下:

步骤 1:引入 fmt/printf.h 头文件(第 5 行)

#include <iostream>
#include <vcclr.h>
#include <fmt/printf.h>

步骤 2:修改 NativeClass::Hello 函数,使用 fmt::println(第 44 行)

void MixedLibrary::NativeClass::Hello(const wchar_t *msg)
{
auto ws = std::wstring(msg);
auto str = std::string(ws.length(), 0);
std::transform(ws.begin(), ws.end(), std::back_inserter(str), [](wchar_t c) { return static_cast<char>(c); });
fmt::println("Hello from NativeClass in MixedLibrary");
fmt::println("-- message: {}", str);
fmt::println("-- printed using FMT version {}", FMT_VERSION);
}

构建应用程序

NativeClass::Hello 函数被 ManagedApp 项目用于通过 C++ 代码向控制台打印消息。上述修改使 CLR 应用能够使用 fmt 库。

无需修改应用项目的代码,直接构建并运行 ManagedApp 项目即可。

程序输出应类似如下内容:

=== Managed class ===
Hello from ManagedClass in MixedLibrary
Hello from NativeClass in MixedLibrary
-- message: from managed app!
-- printed using FMT version 110002

=== P/Invoke ===
Hello from NativeEntryPoint_CallNative in MixedLibrary
Hello from NativeClass in MixedLibrary
-- message: from managed app!
-- printed using FMT version 110002

后续步骤

你还可以学习以下实用功能: