【问题标题】:How to use a C++ project from within a .NET application?如何在 .NET 应用程序中使用 C++ 项目?
【发布时间】:2013-03-07 10:32:26
【问题描述】:

我是一名普通的 .NET 开发人员,旨在将 C++ 库集成到 .NET 项目中。我有一些想法,但由于我一般是 C++ 新手,所以我不知道我正在尝试使用的技术的局限性。 C++ 项目本质上是一个快速的声音渲染器,它可以使用一堆不同的后处理技巧来播放多轨音频文件。所有这一切都很酷,但考虑到我想要集成一个简单的 .NET WinForms 应用程序,事情开始变得一团糟。

  1. 首先,C++ 项目没有 .NET 绑定或 ActiveX/COM 集成。它是一个普通的 'ol MS VC++ 9 项目。如果我要在我的 .NET 应用程序中使用该项目,我将不得不以某种方式 interface 与它,即。创建类实例、设置属性、调用方法、编组数据进出等。

  2. 其次,它可以作为独立的 Windows 应用程序运行,并使用 MS Windows API 管理自己的窗口。这很好,但不知何故,我需要 .NET VM 在后台运行,管理节目并运行我所有的 C# 代码。我不会写 C++,所以我需要坚持使用 C# 来围绕这个 lib 构建一个应用程序。

  3. 第三,无论我是否在与 C++ 库相同的进程中运行,我都需要一种方法来构建/界面/调试这两个独立的应用程序,就好像它们是一个应用程序一样.我没有 C++ 编程背景,除了我编写了几个用于高性能数据操作的 C++ DLL。

所以,有很多问题,不知道如何开始!

  • 我可以完全编译这个库并将它变成一个 VC EXE,但是我如何共同编译我的 .NET 代码呢?或者,如何将 C++ 代码编译到 .NET EXE 中,使其在托管环境中运行?请注意,它不是为此而设计的,如果我试图改变太多,可能会出现异常。

  • 主要问题是与它接口。如何公开一些要从 .NET 访问的 C++ 类?我确切地知道我需要哪些类,我只需要几十个类以及它们的相关方法/属性即可从 .NET 获得。我不介意在 .NET 中手写 包装类 来帮助 .NET VM 理解来回传输的字节的类结构。我希望我可以直接在托管环境中使用 C++ 对象,这样我的大部分代码都可以保留在 .NET 中。

  • 即使我将它作为一个独立的应用程序运行,我是否必须求助于套接字或其他东西来与它通信?这是绝对最糟糕的情况,我会尽一切努力避免这种情况。

感谢任何帮助或指点,我希望我清楚自己和手头的任务,并且我的问题足够具体且可以回答。感谢您的帮助!

编辑:如果我编写包装类或生成它们,我可以使用P/Invoke 创建类实例并调用它们的方法并让它们在后台运行本机 C++ 代码吗?此类 C++ 对象的内存将在哪里存储和管理?在 .NET 堆内还是在堆外?

【问题讨论】:

  • @oefe C DLL to .NET 更直接。答案几乎总是 P/Invoke + 使用可以在 .NET 结构上抛出的属性在 .NET 中重新创建 C 结构。 C++ 到 .NET 可能会更复杂,因为可能涉及非托管对象

标签: c# c++ visual-c++ marshalling wrapper


【解决方案1】:

选项 1

如果您打算使用非托管 C++ 对象,最好在 C++/CLI 中编写一个托管包装器,该包装器与要包装的类具有相同的接口,但有一个托管构造函数和析构函数来清理非托管资源.在 C++/CLI 中,您可以创建非托管对象,就像您必须自己删除的 C++ 一样,或者您可以使用 gcnew 关键字创建托管对象。包装器面向 .NET 的一侧将采用托管对象。包装器面向 Native 的一面可以对您已有的所有非托管数据类型进行操作。

例如,考虑非托管类Frobber。在 C++/CLI 中,您将创建一个名为 ManagedFrobber 的类,该类将具有与 Frobber 相同的所有方法,并包含 Frobber 的私有实例。在ManagedFrobber 构造函数中,您将创建您的私有Frobber 实例。在ManagedFrobber 析构函数中,您将删除您的私有Frobber 实例。托管包装类中的每个方法都会简单地调用私有 Frobber 实例上的方法。

选项 2

如果您打算从 C++ 世界调用的所有函数都是 c 风格的函数(我猜是 C# 意义上的静态函数),那么 P/Invoke 可能是要走的路,因为这样您就不必担心所有包装类的细微差别。例如,如果您使用包装类将数组从托管世界传递到非托管世界,您必须担心将数组固定在适当的位置,这样它就不会在托管世界中被 .NET 运行时移动正在玩弄它。使用 P/Invoke 可以为您处理大部分细微差别。

【讨论】:

  • 使用 选项 1,C++ 代码是否仍能全速运行?或者它会在托管环境中运行并编译为比原始 C++ 慢得多的 CIL?当然,使用包装器对性能的影响可以忽略不计,我只关心实际的 C++ 代码。以及如何编译 C++ 代码?它将作为常规 x86 程序集存储在 DLL 中,并将包装器存储为 CIL 对象吗?还是一切都会成为 CIL 对象?
  • @Geotarget 编写包装类不会涉及重新编译非托管 C++。您将引用现有的编译代码并使用相关的标头。完成后,您将拥有原始的非托管二进制文件、包含引用原始二进制文件的包装类的 C++/CLI DLL,以及引用 C++/CLI DLL 的 C# 可执行文件
【解决方案2】:

您可以将托管 C++ (C++/CLI) 包装器写入非托管 C++ 代码。这将是最灵活的解决方案。基本上,您使用 C++ 编写一个 .NET 程序集,该程序集公开了调用您的非托管代码的托管类。然后你可以从你的 C# 项目中引用这个程序集。

有一篇非常基本的文章如何做到这一点:http://www.windowsdevcenter.com/pub/a/dotnet/2004/03/29/mcpp_part3.html

这也可能会有所帮助: http://www.multicoreconsulting.co.uk/blog/c-snippets/how-to-call-unmanaged-cplusplus-from-csharp/

还有MSDN文章.NET Programming in Visual C++

【讨论】:

  • 你的方法和这个一样吗? codeproject.com/Articles/14180/…
  • 并非如此,“现实生活”托管包装器的一个很好的示例可能是 mpg123 库(考虑到您必须熟悉声音处理:)。如果您下载sourcesports\MSVC++\2008clr 文件夹中有一个。
  • @Geotarget,实际的 C++ 代码性能不会受到影响,它仍然会在“非托管模式”下运行。但是您的托管 C++/CLI 类(以 ref__gc 关键字开头)将由 CLR 使用垃圾收集器和其他东西处理。因此,如果您在托管包装器中使用非托管 C++ 类,则您有责任在其析构函数/终结器中释放非托管内存指针。
  • 作为编译的结果,您可以获得 2 个 DLL:托管 CLR 程序集,它将消耗非托管库。两者都将在其范围内运行。
  • 感谢您的 cmets。您的答案是否与 Pete 的答案中的 选项 1 相同?
猜你喜欢
  • 2011-01-29
  • 2011-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-09
  • 1970-01-01
  • 2013-03-23
相关资源
最近更新 更多