简体   繁体   中英

how to create a c++\cli wrapper for unmanaged c dll

I'm probably confusing myself but haven't done this before and a bit of direction would be very helpful.
I am trying to call some C code from a C# application. I have tried using PInvoke but finding it is a bit tricky. I thought I would try doing a C++\\CLI wrapper.

There are some complicated structs which have double arrays of variable length which was hard to handle with PInvoke .
I've read a bit on how this is done but I can't figure it out. Most of what I've found relates to wrapping C++ instead of C . The C code is already exporting its functions which works already from a Java application and its JNA service. I have the C code, headers, library and dll but would rather not make changes to anything existing so as not upset other consuming applications. The C# application calling it will be 64 bit, most examples are creating win32 libraries, does this matter?

UPDATE: adding code below:
NOTE: this is just one function of several and probably the simplest one but they are all fairly similar.

C HEADER:
typedef struct myStruct_t
{
    double prefix[8];
    int length;
    double array[1];
}
myStruct;

C:
extern "C" __declspec( dllexport ) myStruct *doSomething(const myStruct *input, double a)
{
    myStruct *output;
    //doSomething
    return output;
}

There's really very little difference between wrapping C and C++. You need to make a C++/CLI class library. Then you write functions in a managed C++ ref class that wrap the native code.

For instance, suppose the DLL exports this function:

int sqr(int x)

Then in your class library you would include the header file:

#include <mynativelibrary.h>

You also need to supply the import library to the linker.

Then you can expose the function. The simplest way is to wrap the functions as static methods of the ref class. For instance:

public ref class Class1
{
public:
    static int sqr(int x)
    {
        return ::sqr(x);
    }
};

You can then consume this assembly in your C# code as you would any other assembly.

I have created some projects in VisualStudio 2012 which wraps an old MFC dll with managed code. I did it like this:

  1. Create a class library in as CLR.
  2. Link the old project to this new project
  3. Create wrappers in new project for functions and structures and call the old code.
  4. Use the new object in C# code.

Please don't forget to create Unittesting for the managed C++ code. (I always forget...:) )

Good luck.

If Java can call your C code via jna, then there should be not problem with C# via PInvoke. While C++ interop(using C++/Cli) is one type of PInvoke(Implicit PInvoke), using DllImport is explicit PInvoke.

Implicit PInvoke is useful when you do not need to specify how function parameters will be marshaled, or any of the other details that can be specified when explicitly calling DllImportAttribute, but you need crate an additional C++/CLI Dll.

In both way, you have to deal with marshal native data types to the manage ones, this is unavoidable and painful.

in C#, the struct can be declared as:

[StructLayout(LayoutKind.Sequential)]
    public struct myStruct {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        double prefix[] intersects;

        public int length;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public double[] array;
    }

But for the function, DLLImport can't handle this case because C# can't cannot delete the memory for the unmanaged pointer returned by the function, you can create another wrap function in C to make it use a out parameter to return the result, in this case, the C# code is:

[DllImport("...")]
    public static extern void doSomething([In, Out] myStruct[] results,  myStruct[] input,  int len);

Or you can use C++/CLI interop, as it can handle both native and manage types, so the calling sequence is:

  1. C# code calls this C++/CLI function with managed data types:

    ManagedmyStruct[] doSomething( ManagedmyStruct[] input, double a)

  2. In C++/CLI function domSomething, it calls the native function, the steps are:

    ManagedmyStruct[] doSomething( ManagedmyStruct[] input, double a) {

      //convert the ManagedmyStruct[] input to native type myStruct* input myStruct ret* = doSomething(input, a); //convert ret to managed type ManagedmyStruct[] rets return rets; 

    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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