6

我们正在考虑为一些使用托管 C++ 的遗留 C++ 库编写一个 .Net 可调用包装器。

这一切看起来都很容易。我们有什么需要注意的吗?

4

5 回答 5

6

我发现在 C++/CLI 中包装一些现有的 C++ 库通常很容易,并且遇到的陷阱相对较少。我记得的有:

  • 在同一个可执行文件/DLL 中混合非托管 C++ 代码和 C++/CLI 代码是一个非常糟糕的主意。我在关机时遇到了竞争内存管理器的问题(基本上是 .NET 运行时和常规 C++ 运行时,在关机时清理内存时相互踩踏,导致不确定的行为哪个释放了什么)。我没有将静态遗留 C++ 库链接到 C++/CLI 库,而是创建了一个包含遗留 C++ 的 DLL,并将其与 C++/CLI DLL 链接,从而一劳永逸地解决了这个问题。
  • 如果您的代码使用枚举,则必须将它们包装在适当的 C++/CLI 枚举类中,否则其他 .NET 语言将无法查看和使用它们。
  • C++/CLI 对象只能保存指向非托管 C++ 对象的指针。不幸的是,在某些情况下,这意味着您必须创建薄包装层来处理某些对象。我的“最喜欢的”是我必须以这种方式包装 boost::shared_ptrs(从而添加另一层间接),或者在跨越 .NET/native 边界后将它们放入带有空删除器的 shared_ptrs 中。当您必须处理大量使用这种构造的 API 时,两者都不是很好。RAII 不会跨越这个界限,所以请注意,您将不得不投入一些时间来调整它以匹配 .NET 方式。
  • C++/CLI 不进行多重继承,因此如果您的遗留库正在使用它,您可能必须使用接口等对其进行建模。
  • 内部编组代码似乎能够处理大多数 POD 转换,但您将拥有转换 std::strings 等的查找/借用代码。这段代码已经存在,在 Google 上几分钟应该会显示出来(对不起,不要'目前这里没有任何方便的链接)。
于 2009-01-08T21:00:05.357 回答
2

它非常简单并且运行良好。它比 PInvoke 容易得多。

您需要注意的一件大事是在您的托管标头中没有任何非托管成员,包括私有成员、方法签名等。虽然私有成员是指向托管类型的指针是可以的,只需使用前向声明为您的类。

另外,请注意对象的生命周期。很容易引入内存泄漏,因为许多 .NET 程序员不习惯自己清理。如果您创建的任何包装类包含指针,请确保它们是一次性的,并确保您在托管代码中处置它们。托管 C++ 中 IDisposable 的语法也很奇怪,但它在文档中。

另外,请记住,每次跨越托管/非托管边界时都会受到轻微影响,因此请尝试相应地规划您的界面。如果任何东西在循环中被重复调用,最好将该循环移动到边界上,这样你只穿过一次。除非您正在谈论数百万个电话,否则请不要太担心这一点。

这篇文章是相反的,但它有一些有用的观点。

使用我们的 ManWrap 库在本机 C++ 代码中充分利用 .NET

也可以看看

Visual Studio 2005 中的托管代码
删除托管对象、包装库等

于 2009-01-08T20:51:15.280 回答
1

只是我们遇到的一些问题:

  • 内存/资源生命周期管理(GC/IDisposable 与析构函数)。我认为这是众所周知的,并且 Rob 的帖子有一些关于它的内容,所以我不会在这里详细说明......
  • 字符串编码/解码。如果您的本机代码是 UNICODE 构建,则不必担心太多,但如果不是,请在本机字符串和 .Net 字符串之间转换时注意编码。
  • C++ 不支持 [Conditional("Debug")],这意味着在发布版本中也会调用 Debug.Assert、Debug.Trace 等。请改用传统的 C++ 宏。
  • 64 位支持:.Net 默认生成 32 位或 64 位代码,具体取决于平台,但您的本机代码可能只有 32 位...
于 2009-01-08T21:00:21.487 回答
1

我将添加到每个人已经说过的内容,

pin_ptr wch = PtrToStringChars(字符串);(其中字符串是 System::String)

会成为你的朋友。

您不能直接将非托管类包含到托管类中,但您可以将指向非托管类的指针放在构造函数中并将其新建并在析构函数中将其删除。

我没有遇到 Timo Geusch 提到的在一个 DLL 中混合 C++ 和 C++/CLI 代码的问题。我的 DLL 广泛使用这两种方法都没有问题。

C++/CLI 并不难(如果您了解 C++ 和 .NET)并且效果很好。

于 2009-01-08T22:41:52.063 回答
1

正如其他人所说:98%的时间它只是工作,可调试且快速。

到目前为止,我遇到的超出了提到的范围:

  • 不要将所有遗留的 c++ 代码编译为托管的。有一些文章暗示这会很有用,但通常只是速度较慢。
  • 不要忘记捕获非托管异常并将它们作为托管异常重新抛出。
  • 如果您使用 MFC,请注意您不能使用 .lib 运行时,因此您也将部署 MFC 运行时。
  • OpenMP(一个线程库)不会在 C++/CLI 中运行。
  • 当我们使 C++/CLI dll 依赖于我们自己的代码中的 C# dll 时,我们在 VS2005 中遇到了一些构建问题。

它甚至工作得非常好,以至于我开始编写 C++/CLI 代码来对 C++ 代码运行单元测试。NUnit/Resharper 很乐意在 C++/CLI DLL 中找到并运行单元测试,它可以在任何级别直接调用您的本机代码,甚至可以测试您的模板容器类。

于 2010-02-09T22:06:13.473 回答