教程:在 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 标志:
- 找到“C/C++ > 命令行”配置项;
- 在“附加选项”中添加
/utf-8; - 点击“确定”关闭属性页。
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
后续步骤
你还可以学习以下实用功能: