托管 c++ 库,它是如何工作的?

Posted

技术标签:

【中文标题】托管 c++ 库,它是如何工作的?【英文标题】:Managed c++ library , how does it work? 【发布时间】:2013-05-30 20:18:22 【问题描述】:

我发现有一堆项目(SharpDX 等)正在编译以允许 CLR “管理”本机代码,然后直接从例如:c# 调用该本机代码(c++ 函数)。

我正在尝试弄清楚这是如何工作的。如果你们中的任何人都知道,那就太好了。

到目前为止,我所看到的是,我可以将 C# 项目中的引用添加到“CLR 兼容”C++ 库,就像任何其他 c# 库(无需反射)一样,添加它就像:“使用 myCppLibrary”然后调用任何可用的方法。

当我查看刚刚添加的引用中可用的函数时,我似乎可以从 c# 访问在 C++ 库的头 (.h) 文件中声明为 PUBLIC 的任何内容,因为它们会自动转换为 c#编译器的代码。

所以实际上发生了什么:

我在我的 c++ 库的头文件 (.h) 中声明了公共方法/变量。 我编译它们检查 CLR 管理在库的属性选项卡中是否可用。 (我正在使用 Visual Studio 2012) 编译器为每个带有公共方法/属性的头文件 (.h) 生成 c# 类。

但我不明白的是,当我调用“myCppLibrary”中的 giveNumber() 函数时,该函数的实现在哪里执行?

CLR 是否会自动与 c++ 本机代码相互通信,或者实际发生了什么?

【问题讨论】:

【参考方案1】:

不太确定我是否完全理解您所看到的。但是您很可能会看到错误的副作用。 C++/CLI 编译器能够将任何与 C++03 兼容的本机 C++ 代码编译成 IL。 IL 和抖动都足够灵活以支持这一点。就像 C# 编译器生成的 IL 一样,这种 IL 将在运行时被翻译成机器代码,并以完全相同的方式进行优化。

然而,这使它成为托管代码。此代码创建的任何对象仍然是在非托管内存中分配的本机对象,就像本机 C++ 编译器所做的那样。垃圾收集器完全没有注意到它们。

那么错误是允许将该代码翻译为 IL。您错过了 C++ 代码生成器在编译时花费的额外时间来寻找最佳优化的机器代码。抖动也得到了优化,但它不能做得很好,必须在时间限制下运行。当然,您会在运行时承担 jitting 开销。

本机结构和类将在程序集元数据中可见。但它们只是不透明的值类型,C# 程序无法访问它们的成员。您不能直接从 C# 调用此类本机类的方法。为了可用,C++/CLI 程序必须使用 valueref 上下文关键字声明类型。像 ref class,完全等同于 C# 类。

因此,将本机代码编译为机器代码非常重要,就像常规 C++ 编译器所做的那样。你可以通过编译一个没有/clr 的.cpp 源文件来实现。或者通过使用#pragma unmanaged 和#pragma managed 包装此类代码,在机器代码和IL 生成之间来回切换编译器。您从 C++/CLI 获得的附加值是 ref 类中的方法可以直接使用本机代码,而无需像 pinvoke 那样在 C# 中必须经历的那种麻烦。并且使用原生 C++ 类,这是您在 C# 中完全无法做到的。

【讨论】:

【参考方案2】:

当您在 C++ 中启用 CLR 时,编译器会将 C++ 代码编译为 IL 而不是本机代码。在这种情况下,被称为 C++/CLI 的 C++ 增加了各种功能以允许它使用垃圾收集器,可以很好地控制垃圾收集的内存是否被固定以避免它移动,等等

通常,编写 C++/CLI 是一项专业任务,需要同时了解 C++ 和 C#(可能存在一些细微的差异和陷阱)。通常建议将其用于本机代码和托管代码之间的接口,但它可用于将整个库制作成 .net。

【讨论】:

@usr 我也想知道同样的事情,但如果我搞砸了,也不好意思问。如果您能提供更好的答案,请这样做,我只用了 5 到 10 分钟的时间,并期待可能会出现更好的答案。 我猜有人发现 cpp.exe 可能将 C++ 编译为 IL 是非常不可信的。当我前段时间得知这一点时,我也很惊讶。

以上是关于托管 c++ 库,它是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

如何将本机 C++ 静态库链接到托管 C++ 程序集

在 C++ 中的类中初始化与公共和私有相同的函数:它是如何工作的?

休眠乐观锁定..它是如何工作的?

如何从我的 C++/CLI 代码进入非托管 C++ 库

如何将托管 c++ 库导入 C# 应用程序(目标 .net 版本 3.5)

将非托管 c++ 类库暴露给 c#