用于旧C ++库的托管C ++包装器

时间:2009-01-08 20:23:22

标签: .net c++ managed-c++

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

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

5 个答案:

答案 0 :(得分: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边界后将它们放入带有null删除器的shared_ptrs中。当你必须处理那些使用这种结构的API时,两者都不是很好。 RAII没有越过这个边界,所以要注意,你必须投入一些时间来调整它以匹配.NET方式。
  • C ++ / CLI不进行多重继承,因此如果您的旧库正在使用它,您可能需要使用接口等对其进行建模。
  • 内部编组代码似乎能够处理大多数POD转换,但是您将找到/借用转换std :: strings等的代码。这段代码已经存在,Google上几分钟就应该启动它(对不起) ,暂时没有任何方便的链接)。

答案 1 :(得分:2)

这很简单,效果很好。它比PInvoke容易得多。

您需要注意的重要事项是您的托管标头中没有任何非托管成员,包括私有成员,方法签名等。但是,私有成员可以指向托管类型,只需使用向前你的课程的声明。

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

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

本文采用另一种方式,但它有一些有用的点。

Use Our ManWrap Library to Get the Best of .NET in Native C++ Code

另见

Managed Code in Visual Studio 2005
Deleting Managed Objects, Wrapping a Library, and More

答案 2 :(得分:1)

我们遇到的一些问题:

  • 内存/资源生命周期管理(GC / IDisposable与Destructors)。我认为这是众所周知的,Rob的帖子有几个关于它的内容,所以我不会在这里详细说明......
  • 字符串编码/解码。如果您的本机代码是UNICODE版本,则不必担心这一点,但如果没有,请在本机字符串和.Net字符串之间进行转换时仔细考虑编码。
  • C ++不尊重[Conditional(“Debug”)],这意味着Debug.Assert,Debug.Trace等也将在发布版本中调用。请改用传统的C ++宏。
  • 64位支持:.Net默认生成32或64位代码,具体取决于平台,但您的本机代码可能只有32位...

答案 3 :(得分:1)

我只想补充每个人已经说过的话,

pin_ptr wch = PtrToStringChars(string); (其中string是System :: String)

将成为你的朋友。

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

我没有遇到Timo Geusch在一个DLL中混合C ++和C ++ / CLI代码时提到的问题。我的DLL广泛使用而没有问题。

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

答案 4 :(得分:1)

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

到目前为止,我所遇到的情况:

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

它甚至运行良好,我开始编写C ++ / CLI代码来运行C ++代码的单元测试。 NUnit / Resharper将很高兴地在C ++ / CLI DLL中找到并运行单元测试,它可以直接调用任意级别的本机代码,甚至可以测试模板容器类。