在C# WinForms中使用C++库

8
我正在尝试在OpenTK中使用SPARK粒子系统。 我的项目包含文件夹中的头文件,只有两个头文件,这些文件只包含其他文件,文件夹中还包含源文件。 到目前为止,我已经尝试了几种方法,但都没有成功,以下是我尝试过的方法:
1. P/Invoke 在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. 我使用查找和替换来修复这个问题,然后再次尝试,显示了类,因此我启动了xInterop,并收到相同的错误。 4. 它要求确保dll有效。在验证文件有效后,我启动了程序,生成了链接器错误。
这就是我卡住的地方,这些是我得到的链接器错误: :错误 LNK2019:未解析的外部符号 "void __cdecl SPK::swapParticles(class SPK::Particle &,class SPK::Particle &)" (?swapParticles@SPK@@YAXAAVParticle@1@0@Z),该符号在函数 "private: void __thiscall SPK::Pool::swapElements(class SPK::Particle &,class SPK::Particle &)" (?swapElements@?$Pool@VParticle@SPK@@@SPK@@AAEXAAVParticle@2@0@Z) 中被引用 :错误 LNK2001:未解析的外部符号 "unsigned int SPK::randomSeed" (?randomSeed@SPK@@3IA) :错误 LNK2001:未解析的外部符号 "unsigned long const SPK::NO_ID" (?NO_ID@SPK@@3KB) :错误 LNK2001:未解析的外部符号 "public: static float const * const 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;
}

这就是我的问题,很抱歉我解释得太多了,但我已经搜索了三天也没有找到解决方案。

附言:

这个库非常大,因此自动解决方案是必须的。


我认为你需要学习如何自己进行交互操作,我可以建议一个很好的资源Expert C++/CLI - Chris O
@ChrisO 我不知道为什么,但亚马逊的链接无法打开。 - niceman
这是Marcus Heege所著的Expert C++/CLI。你可能需要知道有两种P/Invoke,显式和隐式。可以参考维基百科上的介绍文章(http://en.wikipedia.org/wiki/Platform_Invocation_Services)来了解这两种类型之间的区别。根据SPK DLL函数签名的不同,可能无法使用显式版本的P/Invoke。一个常见的例子是,如果函数使用std::basic_string,则必须编写自定义的C++/CLI包装器来进行封送处理。 - Chris O
@ChrisO 那就没有其他方法了吗?当你有一个大的库时,编写自定义包装器是很困难的。 - niceman
如果您确实需要使用类,但无法导出它们,那么可以导出作为类包装器的普通函数。例如:https://ideone.com/at1FT5 - rev
显示剩余4条评论
4个回答

5
这是一个非常不友好的C++库,需要使用C++/CLI包装器才能与之交互,无法使用PInvoke。该库有许多小方法和很多类,依赖组合生成效果,所以尝试使用几个大类做交互的方法是行不通的。最大的问题是它严重依赖于多继承,而这在.NET中是不支持的,这将使任何自动生成包装器的工具失效。另外请注意,它仅支持OpenGL渲染,这并不是Windows上非常流行的图形API。虽然该库很有吸引力,并存在相当长的时间,但还没有人成功地将其移植到.NET中,这并不奇怪。据我看来,你没有希望,只有重写才能奏效。

没注意到使用多重继承,+1。 - niceman

4
PInvoke可以实现您想要的功能。只要知道函数签名,无论是否拥有该DLL的代码都没有关系。
请看一下这些来自MSDN和code project的文章,介绍了PInvoke的基础知识:
  1. 平台调用教程
  2. P/Invoke教程:基础(第1部分)
编辑: 有可能会有工具为您生成DllImport签名。我自己没有尝试过任何这样的工具。请参考以下链接:
  1. P/Invoke Signature Generator
  2. 最简单的生成P/Invoke代码的方法?
  3. 这个
  4. http://www.swig.org/
希望这有所帮助。

我可以推荐SWIG,我已经多次使用过它,但通常是用于将自定义C++库绑定到Java。 - baris.aydinoz

1
如果您的本地dll导出了一些类,那么我强烈建议为原始的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文件添加到该项目中。将以下内容添加到该文件中:
EXPORTS 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文件中实现这些函数。完成后,为此DLL创建一个C#包装器并使用它。

我不理解,请举例说明如何编写这三种方法。 - niceman
无论如何,我知道这将产生与我在问题中发布的链接器错误相同的结果。 - niceman
为什么不立即创建一个C#包装器,而要另外创建一个DLL呢?我想使用类,而不仅仅是函数。 - niceman
@niceman,因为类的缘故,你需要一个额外的封装器。据我所知,在C#中无法使用C++类。pContext将保存您导出类的地址。如果您仍然不明白,应该在尝试解决此问题之前阅读一些书籍。但通常这是一项微不足道的任务。 - guest
有没有自动化的方法来做这个额外的包装器?这个库很大,我不想排除函数。 - niceman

1

谢谢,但是这个库还是很大,有没有自动化的(已尝试)方法?而且最好有一个使用该工具的教程。 - niceman
http://stackoverflow.com/questions/4804494/p-invoking-function-via-a-mangled-name - danbo
谢谢danbo,但是自动化的东西是必须的。 - niceman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接