How to pass array contents back and forth between managed (C#) and unmanaged (C++) code

Viewed 315

I'm trying to pass an array of floats from C# to a C++ dll which will do some work on that array and then pass back an answer in the form of an array of float (not necessarily of the same length).

I have code that will pass the array, modify it and then return it, but the values in the array are not as expected though the array appears otherwise well formed...
The example below takes an array of floats, passes it to C++. In C++ a value of 2 is added to every element of the array in order to simulate the work done and the result is returned to C# using a callback.
A different set of values is returned everytime.
A typical set of returned values is:- 81.58881, 2, 75.75787, 4

Any thoughts would be gratefully recieved..
Cheers
Graham

C#

public delegate void CallBack1(IntPtr param, int len);
[DllImport("TestDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void testCPlusSide(CallBack1 cb, IntPtr arr, int len);

private void button_Click(object sender, EventArgs e)
{
    float[] dataIn = new float[] { 0, 0.5f, 7.79f, 46 };
    GCHandle gch = GCHandle.Alloc(dataIn);

    CallBack1 testCB = new CallBack1(TestCallBack);

    testCPlusSide(testCB, GCHandle.ToIntPtr(gch), dataIn.Length);

    gch.Free();
}

void TestCallBack(IntPtr param, int len)
{
    float[] vals = new float[len];
    GCHandle gch = GCHandle.FromIntPtr(param);
    Marshal.Copy(param, vals, 0, len);
    StringBuilder sb = new StringBuilder();
    foreach (float item in vals)
    {
        sb.Append(item.ToString() + "\n");
    }
    MessageBox.Show(sb.ToString());
}

C++

typedef void(__stdcall * CallBack1)(float * arr, int len);
__declspec(dllexport) void testCPlusSide(CallBack1 cb, float* arr, int len)
{
    for (int i = 0; i < len; i++)
    {
        arr[i] += 2;
    }
    cb(arr, len);
}

UPDATE
I added a temporary float to read the contents of the array on the C++ side and I have discovered that the contents of the array C++ side bear no relationship to the values passed - I have no idea why....

1 Answers

I have managed to get things working.
I changed the GCHandle.Alloc method so it made a Pinned Object pointer and everything worked.
I also removed an unnecessary GCHandle call in the TestCallBackFunction The full changed code is:-

C#

public delegate void CallBack1(IntPtr param, int len);
[DllImport("TestDll.dll", CallingConvention = CallingConvention.Cdecl)]  

public static extern void testCPlusSide(CallBack1 cb, IntPtr arr, int len);
    
private void button_Click(object sender, EventArgs e)
{
    float[] dataIn = new float[] { 0, 0.5f, 7.79f, 46 };
    GCHandle gch = GCHandle.Alloc(dataIn,GCHandleType.Pinned);

    CallBack1 testCB = new CallBack1(TestCallBack);

    testCPlusSide(testCB, gch.AddrOfPinnedObject(), dataIn.Length);
    
    gch.Free();
}
    
void TestCallBack(IntPtr param, int len)
{
    float[] vals = new float[len];
    Marshal.Copy(param, vals, 0, len);
    StringBuilder sb = new StringBuilder();
    foreach (float item in vals)
    {
        sb.Append(item.ToString() + "\n");
    }
    MessageBox.Show(sb.ToString());
}

C++

typedef void(__stdcall * CallBack1)(float * arr, int len);

__declspec(dllexport) void testCPlusSide(CallBack1 cb, float* arr, int len)
{
    for (int i = 0; i < len; i++)
    {
        arr[i] += 2;
    }
    cb(arr, len);
}

Thanks to Carlos and rhughes for your help
Cheers
Graham

Related