Search code examples
c#c++marshallingdllexport

How to export C++ data to a C# struct?


I'm struggling to export a C++ struct data to C#.

Say I have the following structure representing a 3-floating-points vector:

// C++
struct fvec3
{
public:
    float x, y, z;
    fvec3(float x, float y, float z) : x(x), y(y), z(z) { }
};

// C#
[StructLayout(LayoutKind.Sequential)]
struct fvec3
{
    public float x, y, z;

    public fvec3(float x, float y, float z)
    {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

Now, if I wanted to use a fvec3 from C# to C++, I can use the following without a problem:

// C++
__declspec(dllexport) void Import(fvec3 vector)
{
    std::cout << vector.x << " " << vector.y << " " << vector.z;
}

// C#
[DllImport("example.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Import(fvec3 vector);

...

Import(new fvec3(1, 2, 3)); // Prints "1 2 3".

The problem now is to do the opposite: return a C++ fvec3 to C#. How can I do this? I've seen many C# implementations use something along the lines of this:

// C#
[DllImport("example.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Export(out fvec3 vector);

...

fvec3 vector;
Export(out vector); // vector contains the value given by C++

But how do I write the C++ Export function?

I tried everything I could think about for both the signature and the body:

// Signatures: 
__declspec(dllexport) void Export(fvec3 vector)
__declspec(dllexport) void Export(fvec3* vector)
__declspec(dllexport) void Export(fvec3& vector)

// Bodies (with the pointer variants)
vector = fvec3(1, 2, 3);
memcpy(&fvec3(1, 2, 3), &vector, sizeof(fvec3));
*vector = new fvec(1, 2, 3);

Some of these have no effet, some return garbage values, and some cause crashes.


Solution

  • ref and out parameters are usually matched with pointer arguments.

    Try this:

    __declspec(dllexport) void Export(fvec3 *vector)
    {
        *vector = fvec3(1, 2, 3);
    }
    

    (Untested)


    Alternatively, you should be able to simply return a fvec3 from your function:

    // C++
    __declspec(dllexport) fvec3 Export(void)
    {
        return fvec3(1, 2, 3);
    }
    
    // C#
    [DllImport("example.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern fvec3 Export();
    
    ...
    
    fvec3 vector = Export();
    

    (Untested)