8

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

1. P/调用

这是在您的 C++ 项目中编写一些代码,这些代码构建了 dll,然后使用DllImportC# 中的属性(显然需要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(class SPK::Particle &,class SPK::Particle &)”(?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: 无法解析的外部符号 "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;
}

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

PS:

图书馆非常大,因此必须使用自动解决方案。

4

4 回答 4

5

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

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

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

于 2015-01-07T21:01:10.400 回答
4

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

查看 MSDN 中的这些文章和涵盖 PInvoke 基础的代码项目:

  1. 平台调用教程
  2. P/Invoke 教程:基础知识(第 1 部分)

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

  1. P/Invoke 签名生成器
  2. 生成 P/Invoke 代码的最简单方法是什么?
  3. 这个
  4. http://www.swig.org/

希望有帮助。

于 2014-11-18T04:47:36.680 回答
1

如果您的本机 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 创建一个包装器并使用它。

于 2015-01-04T18:29:06.550 回答
1

嗯 P/Invoke 调用 GetProcessAdress .. 所以导入 ABI 问题是如此..

http://www.codeproject.com/Articles/18032/How-to-Marshal-aC-Class 这是你的答案,感谢那些人

于 2015-01-07T20:43:34.067 回答