Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a C++ library in C# winforms

I'm trying to use the SPARK particle system in OpenTK.
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

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

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:

This is a good approach, especially with 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". 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.

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.
  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. 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).
  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.
  4. It asked to ensure that the dll works. 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)

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:

the library is very big, so an automatic solution is a must.

like image 246
niceman Avatar asked Nov 18 '14 02:11

niceman


4 Answers

This is a very, very unfriendly C++ library to have to interop with. Scratch the idea that pinvoke can work, C++ classes require C++/CLI wrappers. 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. Also note that it only supports OpenGL rendering, not a terribly popular graphics api on Windows.

The library is attractive, and has been around for quite a while, but nobody has successfully ported it to .NET yet. This is unsurprising. In my opinion, you don't stand a chance. Only a rewrite could work.

like image 195
Hans Passant Avatar answered Sep 20 '22 14:09

Hans Passant


PInvoke is the way to do what you are looking for. Doesn't matter if you have or do't have the code for that DLL so long you know the function signature.

Have a look at these articles from MSDN and code project that cover basics of PInvoke:

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

Edit: There are tools that can possibly generate DllImport signature for you. I have NOT tried any of these myself. Have a look:

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

Hope that helps.

like image 36
silverspoon Avatar answered Sep 18 '22 14:09

silverspoon


If your native dll exports some classes, then I would strongly suggest creating another native DLL wrapper for the original one. 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. Inside my_lib_do_something() cast the pContext to a pointer of your class type and use it.

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

EDIT:
Regarding that part on how to do it:
Create a new C++ solution/project, select DLL type. Then add .def file to that project. Add to that file this:

EXPORTS
my_lib_create_context @1
my_lib_delete_context @2
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. Once you are done, create a wrapper in C# for this DLL and use it.

like image 35
guest Avatar answered Sep 20 '22 14:09

guest


Hmm P/Invoke call GetProcessAdress .. so importing ABI problem is so so..

http://www.codeproject.com/Articles/18032/How-to-Marshal-a-C-Class here are your answer give credit to those guy

like image 44
danbo Avatar answered Sep 18 '22 14:09

danbo