Search code examples
c#c++pointerspinvokemarshalling

C++ DLL returns unexpected array values when called from C# using P/Invoke


I'm creating a C++ DLL library to be used in a C# application through P/Invoke. The C++ library exposes a function like this:

int getData(unsigned char *ptr) {
    ptr = new unsigned char[5];
    ptr[0] = 10;
    ptr[1] = 20;
    ptr[2] = 30;
    ptr[3] = 40;
    ptr[4] = 50;
    return 0;
}

where ptr is a pointer to an array of length 5. In C#, I have the following code:

using System.Runtime.InteropServices;
    
[DllImport(CppLibPath, CallingConvention = CallingConvention.Cdecl)]
private static extern int getData(ref IntPtr ptr);
    
public static void GetData() {
    var size = 5;
    var ptr = Marshal.AllocHGlobal(size);
    var arr = new byte[size];
    var result = getData(ref ptr, shouldDetect);
    Marshal.Copy(ptr, arr, 0, size);
    Marshal.FreeHGlobal(ptr);
}

I expect arr to be an array of size 5 containing the values 10, 20, 30, 40, and 50. However, the actual values are different and seem to change every time I run the C# script. Can anyone help me understand why this is happening?


Solution

  • ptr = new unsigned char[5]; is just wrong: C# has no way of releasing that memory, and it's just overwriting the original pointer. So remove that line from the code in C++. The pointer will already be set by C#.

    On the C# side, you can use an array, instead of doing custom marshalling.

    [DllImport(CppLibPath, CallingConvention = CallingConvention.Cdecl)]
    private static extern int getData
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
      byte[] ptr
    );
    
    public static void GetData()
    {
        var arr = new byte[size];
        var result = getData(arr);
    }