教程:在 CLR 应用程序中安装本机依赖项

C++/CLI 是一种技术,可用于将 .NET 类与本机 C++ 类型相结合,以创建采用 C++ 代码并使其可供 .NET 程序访问的库和应用程序。

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

在本教程中,您将学习如何执行以下操作:

先决条件

生成示例 C++/CLI

在本教程中,我们将从现有的 C++/CLI 应用程序开始,并添加随 vcpkg 一起安装的 C++ 依赖项。 本教程中的命令假定正在适用于 Visual Studio 的 开发人员 PowerShell 中运行它们。

1 - 克隆示例存储库

第一步是在 .NET 示例存储库中获取 C++/CLI 示例应用程序的副本。 C++/CLI 示例应用程序位于 core/interop/cpp-cli 文件夹中。

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

2 - 导航到示例应用程序文件夹

cd samples/core/interpo/cpp-cli

2 - 检查项目是否正确生成并运行

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

  • ManagedLibrary:适用于 .NET 的 C# 库
  • MixedLibrary:将 ManagedLibrary 中的本机 C++ 代码和 .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!

3 - 在 Visual Studio 中打开项目

在后续步骤中,我们将修改库以使用 fmt 将消息打印到控制台。 将通过 vcpkg 安装 fmt 库,并在使用项目中链接该库。

若要编辑源文件,请在 Visual Studio 中打开 CPP-CLI.sln 解决方案:

start CPP-CLI.sln

在 Visual Studio 中打开解决方案后,可能会收到将项目重定向到最新版本的提示。 可以单击“确定”来将 Windows SDK 版本和平台工具集升级到最新版本。

重新定向到现有项目

项目的目标 Windows SDK 版本和平台工具集版本到最新版本。

添加 C++ 本机依赖项

接下来,我们将对 MixedLibrary 项目进行以下更改。

  • 添加 vcpkg 清单以获取 fmt
  • 在项目中启用 vcpkg。
  • NativeClass::Hello 修改为使用 fmt 打印消息。

1 - 创建 vcpkg 清单

右键单击 NativeLibrary 项目,然后单击上下文菜单中的“添加 > 新项”。

命名新项 vcpkg.json,这是 vcpkg 清单文件,并确保该文件是在项目文件夹的根目录中创建的。

2 - 将 fmt 添加为依赖项

打开 vcpkg.json 文件并编辑其内容以匹配以下内容:

{
  "dependencies": [ "fmt" ]
}

如果 vcpkg 清单文件位于正确的位置并且尝试生成项目,则会收到以下警告:

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

3 - 在 MixedLibrary 的属性中启用 vcpkg

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

更改 vcpkg 部分中的以下属性:

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

MixedLibrary 项目属性

启用 vcpkg 所需

进行这些更改后,Visual Studio 现在将读取 vcpkg.json 文件,并在生成项目之前自动安装清单中包含的依赖项。

在“属性”页面中,我们还希望启用 /utf-8 标志,以便正确创建 fmt 生成。

在 C/C++ 设置的“命令行”子部分中,编辑其他选项以将 /utf-8 包含在以下项中:

最后,单击“确定”关闭“属性”页面。

4 - 验证 vcpkg 是否正常工作

如果正确配置了所有内容,Visual Studio 将在生成 MixedLibrary 项目之前,调用 vcpkg 来安装依赖项。

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

如果未看到 vcpkg 输出,或者 fmt 无法生成,请确保正确执行上述步骤,包括在 C/C++ > 命令行中的“其他选项”中添加 /utf-8

4 - 修改项目的源代码

最后,我们希望将 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

后续步骤

另请了解其他有用的功能: