简体   繁体   English

在C#winforms中使用C ++库

[英]Using a C++ library in C# winforms

I'm trying to use the SPARK particle system in OpenTK. 我正在尝试在OpenTK中使用SPARK粒子系统。
my project contains the header files in folders, with just two header files which only includes the others, and the folders contain the source files too. 我的项目包含文件夹中的头文件,只有两个头文件只包含其他文件,文件夹也包含源文件。
I have tried several approaches so far, but nothing has worked for me yet, those are what I've tried: 到目前为止,我已尝试了几种方法,但对我来说没有任何效果,这些都是我尝试过的:

1. P/Invoke 1. P / Invoke

This is writing some code in your C++ project which built the dll and then using the DllImport attribute in C# (which obviously needs using System.Runtime.InteropServices; ). 这是在你的C ++项目中编写一些代码,它们构建了dll,然后在C#中使用了DllImport属性(显然需要using System.Runtime.InteropServices; )。 I discovered the hard way that this doesn't work with classes, it only works for methods outside classes, so this approach was ineffective. 我发现这种方法不适用于类很难,它只适用于类外的方法,因此这种方法无效。

2. Wrapper classes 2.包装类

This is writing a class that contains a pointer to the original class. 这是编写一个包含指向原始类的指针的类。 I discovered that the difficulty actually arises from calling unmanaged code(no automatic memory management) from managed code, that's why wrapper classes are needed, and that's why you have to redefine methods' signatures and let them call the original methods. 我发现实际上困难来自于从托管代码调用非托管代码(没有自动内存管理),这就是为什么需要包装类,这就是为什么你必须重新定义方法的签名并让它们调用原始方法。

Of course this has advantages, like naming the classes and methods in a better way, but the library is so big so you can see the effort of this. 当然这有一些优点,比如以更好的方式命名类和方法,但是库很大,所以你可以看到它的努力。

3. Use of an automatic wrapper: 3.使用自动包装机:

This is a good approach, especially with xInterop++. 这是一种很好的方法,特别是对于xInterop ++。 I was really optimistic about this and thought it would work, it says "give me the .h files and the dll and I'll build the .NET dll for you". 我对此非常乐观,并认为它会起作用,它说“给我.h文件和dll,我将为你构建.NET dll”。 Good but doing so gives an error; 很好,但这样做会给出错误; in brief: 简单来说:

You must make sure .h files and the dll are consistent and that the library works in a C++ project. 您必须确保.h文件和dll是一致的,并且该库在C ++项目中工作。

I have tried several things to deal with this error: 我已经尝试了几件事来处理这个错误:

  1. Knowing what the dll contains: it is difficult as I learned from Googling and from this site, so my try failed. 知道dll包含什么:我从谷歌搜索和从这个网站学到的很难,所以我的尝试失败了。
  2. Putting header files in a new project and building it: received errors, fixed them, and then built the project and it worked well. 将头文件放在一个新项目中并构建它:收到错误,修复它们,然后构建项目,它运行良好。 I uploaded the dll file with the header files to xInterop. 我将带有头文件的dll文件上传到xInterop。 It then told the classes that were found but would then state that nothing was found! 然后它告诉已发现的类,但会说没有找到任何东西! I searched and learned that the compiler must be told which classes are needed to be exposed by the dll by marking every class that is needed using the following statement: _declspec(dllexport) . 我搜索并了解到编译器必须通过使用以下语句标记所需的每个类来告知dll需要暴露哪些类: _declspec(dllexport)
  3. I used Find & Replace to fix this thing and tried again and classes were shown, so I launched xInterop and received the same error. 我使用Find&Replace来修复这个东西并再次尝试并显示了类,所以我启动了xInterop并收到了同样的错误。
  4. It asked to ensure that the dll works. 它要求确保dll正常工作。 After verifying that the file worked I launched the program and linker errors were produced. 验证文件有效后,我启动了程序并生成了链接器错误。

Here is where I'm stuck, these are the linker errors I get: 这是我被卡住的地方,这些是我得到的链接器错误:

main.obj : error LNK2019: unresolved external symbol "void __cdecl SPK::swapParticles(class SPK::Particle &,class SPK::Particle &)" (?swapParticles@SPK@@YAXAAVParticle@1@0@Z) referenced in function "private: void __thiscall SPK::Pool::swapElements(class SPK::Particle &,class SPK::Particle &)" (?swapElements@?$Pool@VParticle@SPK@@@SPK@@AAEXAAVParticle@2@0@Z) main.obj : error LNK2001: unresolved external symbol "unsigned int SPK::randomSeed" (?randomSeed@SPK@@3IA) main.obj : error LNK2001: unresolved external symbol "unsigned long const SPK::NO_ID" (?NO_ID@SPK@@3KB) main.obj : error LNK2001: unresolved external symbol "public: static float const * const SPK::Transformable::IDENTITY" (?IDENTITY@Transformable@SPK@@2QBMB) main.obj:错误LNK2019:未解析的外部符号“void __cdecl SPK :: swapParticles(类SPK :: Particle&,类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)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)

This is the code that produced those errors: 这是产生这些错误的代码:

#include "Extensions/Emitters/SPK_RandomEmitter.h"

using namespace SPK;

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

So that's my problem, I'm sorry for explaining too much but I've done a three days search without finding any solution. 所以这是我的问题,我很抱歉解释太多,但我已经做了三天搜索而没有找到任何解决方案。

PS: PS:

the library is very big, so an automatic solution is a must. 图书馆非常大,所以自动解决方案是必须的。

This is a very, very unfriendly C++ library to have to interop with. 这是一个非常非常不友好的C ++库,必须与之互操作。 Scratch the idea that pinvoke can work, C++ classes require C++/CLI wrappers. 抓住pinvoke可以工作的想法,C ++类需要C ++ / CLI包装器。 There are a great many classes with many small methods. 有很多类有许多小方法。 The library depends on composition to generate effects so any approach that tries to do the interop with a few God classes is a dead avenue. 图书馆依赖于构图来产生效果,因此任何尝试与少数上帝类进行互操作的方法都是一条死路。

The most significant hazard is that it heavily relies on multiple inheritance. 最重要的危害是它严重依赖于多重继承。 Not supported in .NET, this will defeat any tool that auto-generate wrappers. 在.NET中不支持,这将打败任何自动生成包装器的工具。 Also note that it only supports OpenGL rendering, not a terribly popular graphics api on Windows. 另请注意,它仅支持OpenGL渲染,而不是Windows上非常流行的图形API。

The library is attractive, and has been around for quite a while, but nobody has successfully ported it to .NET yet. 该库很有吸引力,已经存在了很长一段时间,但是还没有人成功将它移植到.NET。 This is unsurprising. 这并不奇怪。 In my opinion , you don't stand a chance. 在我看来 ,你没有机会。 Only a rewrite could work. 只有重写才能奏效。

PInvoke is the way to do what you are looking for. PInvoke是您正在寻找的方式。 Doesn't matter if you have or do't have the code for that DLL so long you know the function signature. 如果您知道函数签名,那么如果您拥有或没有该DLL的代码,则无关紧要。

Have a look at these articles from MSDN and code project that cover basics of PInvoke: 看看MSDN和代码项目中的这些文章,它们涵盖了PInvoke的基础知识:

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

Edit: There are tools that can possibly generate DllImport signature for you. 编辑:有些工具可能会为您生成DllImport签名。 I have NOT tried any of these myself. 我自己没有尝试过这些。 Have a look: 看一看:

  1. P/Invoke Signature Generator P / Invoke签名生成器
  2. Easiest way to generate P/Invoke code? 生成P / Invoke代码的最简单方法是什么?
  3. This one 这个
  4. http://www.swig.org/ http://www.swig.org/

Hope that helps. 希望有所帮助。

If your native dll exports some classes, then I would strongly suggest creating another native DLL wrapper for the original one. 如果您的本机dll导出一些类,那么我强烈建议为原始类创建另一个本机DLL包装器。 It should export a few functions and no classes at all. 它应该导出一些函数而根本不导出任何类。

Exported functioned could be something like: 导出的函数可能是这样的:

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 );

Inside my_lib_create_context() create an instance of your class and pass the pointer back through the ppContext parameter. my_lib_create_context()内部创建一个类的实例,并通过ppContext参数传回指针。 Inside my_lib_do_something() cast the pContext to a pointer of your class type and use it. my_lib_do_something()内部,将pContext转换为类类型的指针并使用它。

Also, when writing your wrapper, pay attention to calling convention, because you will need to pass that information to the .NET world (I think stdcall is default if not explicitly defined). 另外,在编写包装器时,请注意调用约定,因为您需要将该信息传递给.NET世界(我认为如果没有明确定义,stdcall是默认的)。

EDIT: 编辑:
Regarding that part on how to do it: 关于如何做到这一点:
Create a new C++ solution/project, select DLL type. 创建一个新的C ++解决方案/项目,选择DLL类型。 Then add .def file to that project. 然后将.def文件添加到该项目。 Add to that file this: 添加到该文件:

EXPORTS EXPORTS
my_lib_create_context @1 my_lib_create_context @ 1
my_lib_delete_context @2 my_lib_delete_context @ 2
my_lib_do_something @3 my_lib_do_something @ 3

Then add some header file where you will put function signatures like this: 然后添加一些头文件,你将把函数签名放在这里:

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...
}

Implement these functions in .cpp file. 在.cpp文件中实现这些功能。 Once you are done, create a wrapper in C# for this DLL and use it. 完成后,在C#中为此DLL创建一个包装器并使用它。

Hmm P/Invoke call GetProcessAdress .. so importing ABI problem is so so.. 嗯P / Invoke调用GetProcessAdress ..因此导入ABI问题是如此..

http://www.codeproject.com/Articles/18032/How-to-Marshal-aC-Class here are your answer give credit to those guy http://www.codeproject.com/Articles/18032/How-to-Marshal-aC-Class这里是你的答案给予那些家伙的信任

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM