Marshalling float Array to c#

2020-05-07 14:47发布

问题:

I'm trying to marshal a struct that contains a float-Array from a C++ DLL to C#.

I created the C++ DLL from the following code:

//MarshalTest.h
namespace mTest{

    typedef struct {

        float data[3];
        int otherStuff;

    } dataStruct;

    extern "C" __declspec(dllexport) dataStruct getData();
}

//MarshalTest.cpp
#include "MarshallTest.h"

using namespace std;

namespace mTest{
    dataStruct getData(){
        dataStruct d = {{ 16, 2, 77 }, 5};
        return d;
    }
}

I use the following code to make the getData-Function available in C#:

public unsafe struct dataStruct{
    public fixed byte data[3];
    public int otherStuff;

    public unsafe float[] Data{
        get{
            fixed (byte* ptr = data){
                IntPtr ptr2 = (IntPtr)ptr;
                float[] array = new float[3];

                Marshal.Copy(ptr2, array, 0, 3);
                return array;
            }
        }

        set{
            fixed (byte* ptr = data){
                //not needed
            }
        }
    }
}

[DllImport("MarshallTest", CallingConvention = CallingConvention.Cdecl)]
private static extern dataStruct getData ();

When printing data[] in C# I get the following output: 1.175494E-38 1.610935E-32 8.255635E-20

What am I doing wrong?

回答1:

You must use the right type:

public unsafe struct dataStruct2
{
    public fixed float data[3];
    public int otherStuff;

    public unsafe float[] Data
    {
        get
        {
            fixed (float* ptr = data)
            {
                float[] array = new float[3];

                Marshal.Copy((IntPtr)ptr, array, 0, 3);
                return array;
            }
        }
    }
}

Note that for a small array, you can even use:

public struct dataStruct
{
    public float data1;
    public float data2;
    public float data3;
    public int otherStuff;

    public float[] Data
    {
        get
        {
            return new[] { data1, data2, data3 };
        }
    }
}

without using unsafe code.



回答2:

I'm not sure why you are doing all the unsafe stuff and pointer manipulation. If you correctly define your struct, its pretty simple:

[StructLayout(LayoutKind.Sequential)]
public struct dataStruct
{
    [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)]
    public float[] data;
    public int otherStuff;
}

You just need to instruct the interop marshalling with some additional information about your type, then you can get unmanaged data without using unsafe constructs and pointers. There are very, very few reasons to use pointers in C#.

Yours probably isn't working because you are using a byte[] array as the first parameter, so when copying the data in from the unmanaged side you are overwriting a lot of data (maybe you think using fixed turns them into pointers?). The types have to match so that the struct matches and the data is copied in correctly. Also since C# will automatically re-arrange a struct to best suit memory, you may not be placing data were you think. Thats what the StructLayout does at the top of the example above, it tells the compiler to keep the structure as its defined sequentially.