【问题标题】:Using a C++ library in C# winforms在 C# winforms 中使用 C++ 库
【发布时间】:2014-11-18 02:30:09
【问题描述】:

我正在尝试在 OpenTK 中使用 SPARK 粒子系统。
我的项目包含文件夹中的头文件,只有两个头文件,其中仅包含其他头文件,并且文件夹也包含源文件。
到目前为止,我已经尝试了几种方法,但没有任何方法对我有用,这些是我尝试过的:

1。 P/调用

这是在您的 C++ 项目中编写一些代码来构建 dll,然后在 C# 中使用DllImport 属性(显然需要using System.Runtime.InteropServices;)。我发现这不适用于类的困难方式,它只适用于类外的方法,所以这种方法是无效的。

2。包装类

这是编写一个包含指向原始类的指针的类。我发现困难实际上来自于从托管代码调用非托管代码(没有自动内存管理),这就是需要包装类的原因,这就是为什么您必须重新定义方法的签名并让它们调用原始方法的原因。

当然这有好处,比如以更好的方式命名类和方法,但是库太大了,你可以看到这样做的努力。

3。使用自动包装器:

这是一个很好的方法,尤其是对于 xInterop++。我对此非常乐观,并认为它会起作用,它说“给我 .h 文件和 dll,我会为你构建 .NET dll”。很好,但这样做会出错;简而言之:

您必须确保 .h 文件和 dll 是一致的,并且 库在 C++ 项目中工作。

我已经尝试了几种方法来处理这个错误:

  1. 了解 dll 包含的内容:我从谷歌搜索和从这个网站了解到这很困难,所以我的尝试失败了。
  2. 将头文件放入新项目并构建它:收到错误,修复它们,然后构建项目,它运行良好。我将带有头文件的 dll 文件上传到 xInterop。然后它告诉找到的类,但会说什么也没找到!我搜索并了解到编译器必须通过使用以下语句标记所需的每个类来告诉编译器需要由dll公开哪些类:_declspec(dllexport)
  3. 我使用 Find & Replace 修复了这个问题并再次尝试并显示了类,所以我启动了 xInterop 并收到了同样的错误。
  4. 它要求确保 dll 正常工作。在验证文件工作正常后,我启动了程序并产生了链接器错误。

这是我卡住的地方,这些是我得到的链接器错误:

main.obj:错误 LNK2019:无法解析的外部符号“void __cdecl SPK::swapParticles(类 SPK::粒子 &,类 SPK::粒子 &)" (?swapParticles@SPK@@YAXAAVParticle@1@0@Z) 在函数中引用 “私有:void __thiscall SPK::Pool::swapElements(类 SPK::Particle &,类 SPK::Particle &)" (?swapElements@?$Pool@VParticle@SPK@@@SPK@@AAEXAAVParticle@2@0@Z) main.obj:错误 LNK2001:未解析的外部符号“unsigned int” SPK::randomSeed" (?randomSeed@SPK@@3IA) main.obj:错误 LNK2001: 未解析的外部符号“unsigned long const SPK::NO_ID” (?NO_ID@SPK@@3KB) main.obj:错误 LNK2001:未解析的外部符号 “公共:静态浮点常量 * 常量 SPK::Transformable::IDENTITY” (?IDENTITY@Transformable@SPK@@2QBMB)

这是产生这些错误的代码:

#include "Extensions/Emitters/SPK_RandomEmitter.h"

using namespace SPK;

int main()
{   
    RandomEmitter e;
    e.changeFlow(6);
    e.getFlow();
    return 0;
}

所以这是我的问题,我很抱歉解释太多,但我已经进行了三天的搜索,但没有找到任何解决方案。

PS:

库很大,所以必须使用自动解决方案。

【问题讨论】:

  • 我认为你将不得不自己学习如何进行互操作,我可以推荐一个优秀的资源Expert C++/CLI
  • @ChrisO 我不知道为什么,但是亚马逊链接打不开。
  • Marcus Heege 的专家 C++/CLI。它可能会帮助您了解 P/Invoke 实际上有两种风格,显式和隐式。 See this intro article on Wikipedia 了解这两种类型之间的差异的一个很好的起点。根据 SPK DLL 的函数签名,可能无法使用显式版本的 P/Invoke。一个常见的例子,如果函数使用std::basic_string,那么你将不得不编写一个自定义的 C++/CLI 包装器来进行封送处理。
  • @ChrisO 所以没办法了?当你有一个大库时,编写自定义包装器很困难。
  • 如果您确实需要使用类并且您无法导出它们,您可以将充当包装器的普通函数导出到类。示例:ideone.com/at1FT5

标签: c# c++ dll wrapper language-interoperability


【解决方案1】:

这是一个非常非常不友好的 C++ 库,必须与之互操作。从头开始认为 pinvoke 可以工作,C++ 类需要 C++/CLI 包装器。有很多类有很多小方法。该库依赖于组合来生成效果,因此任何尝试与几个上帝类进行互操作的方法都是死路。

最大的危险是它严重依赖多重继承。在 .NET 中不受支持,这将破坏任何自动生成包装器的工具。另请注意,它仅支持 OpenGL 渲染,而不是 Windows 上非常流行的图形 api。

这个库很有吸引力,并且已经存在了很长一段时间,但是还没有人成功地将它移植到 .NET。这不足为奇。在我的意见中,你没有机会。只有重写才能起作用。

【讨论】:

  • 嗯,没注意到多继承的使用,+1
【解决方案2】:

PInvoke 是执行您正在寻找的事情的方法。只要您知道函数签名,您是否拥有该 DLL 的代码都没关系。

看看这些来自 MSDN 的文章和涵盖 PInvoke 基础知识的代码项目:

  1. Platform Invoke Tutorial
  2. P/Invoke Tutorial: Basics (Part 1)

编辑: 有些工具可能会为您生成 DllImport 签名。我自己没有尝试过任何这些。看看:

  1. P/Invoke Signature Generator
  2. Easiest way to generate P/Invoke code?
  3. This one
  4. http://www.swig.org/

希望对您有所帮助。

【讨论】:

  • 我可以推荐 SWIG,多次使用,但通常用于自定义 C++ 库的 Java 绑定。
【解决方案3】:

如果您的本机 DLL 导出了一些类,那么我强烈建议为原始类创建另一个本机 DLL 包装器。它应该导出一些函数,而根本没有类。

导出的函数可能类似于:

my_lib_create_context( void ** const ppContext );
my_lib_delete_context( void * const pContext );
my_lib_do_something( void * const pContext, T param1, T param2 );

my_lib_create_context() 内部创建一个类的实例,并通过ppContext 参数将指针传回。 在my_lib_do_something() 内部,将 pContext 转换为您的类类型的指针并使用它。

另外,在编写包装器时,请注意调用约定,因为您需要将该信息传递给 .NET 世界(如果没有明确定义,我认为 stdcall 是默认值)。

编辑:
关于如何做的那部分:
创建一个新的 C++ 解决方案/项目,选择 DLL 类型。然后将 .def 文件添加到该项目。添加到该文件中:

出口
my_lib_create_context @1
my_lib_delete_context @2
my_lib_do_something @3

然后添加一些头文件,您将在其中放置函数签名,如下所示:

typedef void * SomeContext;

extern "C"
{
  int __stdcall my_lib_create_context( /* [ out ] */ SomeContext * ppContext );
  int __stdcall my_lib_delete_context( /* [ in ] */ SomeContext pContext );
  // TO DO: ... you get it by now...
}

在 .cpp 文件中实现这些功能。完成后,在 C# 中为此 DLL 创建一个包装器并使用它。

【讨论】:

  • 没看懂,举例说明,这三个方法怎么写
  • 无论如何我知道这会产生与我在问题中发布的相同的链接器错误
  • 为什么不立即创建一个 c# 包装器,为什么还要创建另一个 dll。我想使用类,我不想只使用函数
  • @niceman,由于这些类,您需要一个额外的包装器。您不能在 C# AFAIK 中使用 C++ 类。 pContext 将保存您导出的类的地址。如果你仍然不明白,你应该在尝试解决这个问题之前阅读一些书籍。但总的来说,这是一项微不足道的任务。
  • 是否有自动的东西来做这个额外的包装,库很大,我不想排除函数
【解决方案4】:

嗯 P/Invoke 调用 GetProcessAdress .. 所以导入 ABI 问题就是这样..

http://www.codeproject.com/Articles/18032/How-to-Marshal-a-C-Class 这是你的答案给那些家伙的功劳

【讨论】:

  • 谢谢,但图书馆又大了,有没有自动(尝试过)的东西?还有一个使用那个东西的教程会更好。
  • 谢谢你 danbo 但再次强调,自动的东西是必须的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
  • 1970-01-01
相关资源
最近更新 更多