Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array from C++ to C#

I am trying to pass a double array (its actually a std::vector, but converted at transfer) from a c++ dll into a c# script (unity). Using the approach outlined here https://stackoverflow.com/a/31418775.

I can successfully get the size of the array printing on my console in unity however I am not able to use "CoTaskMemAlloc" to allocate memory for the array since I am using Xcode and it doesnt seem to have COM.

For a little more background this array is part of a control for a GUI, c++ creates it and the user edits with the c# GUI - so the plan is to be able to pass the array back to c++ when it has been edited.

C++ code
extern "C" ABA_API void getArray(long* len, double **data){
    *len = delArray.size();
    auto size = (*len)*sizeof(double);
    *data = static_cast<double*>(CoTaskMemAlloc(size));
    memcpy(*data, delArray.data(), size);
}

C# code 
[DllImport("AudioPluginSpecDelay")]
private static extern void getArray (out int length,[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] out double[] array);

int theSize;
double[] theArray;
getArray(out theSize, out theArray);

If I leave out the code concerning the array, the int passes just fine. So I beleive the method to be the right one, its just getting around the lack of CoTaskMemAlloc.

like image 506
k9256 Avatar asked Mar 25 '16 16:03

k9256


2 Answers

You should be able to allocate memory in XCode using malloc and free it in C# using Marshal.FreeCoTaskMem. To be able to free it however, you need to have the IntPtr for it:

C++ code

extern "C" ABA_API void getArray(long* len, double **data)
{
    *len = delArray.size();
    auto size = (*len)*sizeof(double);
    *data = static_cast<double*>(malloc(size));
    memcpy(*data, delArray.data(), size);
}

C# code

[DllImport("AudioPluginSpecDelay")]
private static extern void getArray(out int length, out IntPtr array);

int theSize;
IntPtr theArrayPtr;
double[] theArray;

getArray(out theSize, out theArrayPtr);
Marshal.Copy(theArrayPtr, theArray, 0, theSize);
Marshal.FreeCoTaskMem(theArrayPtr);

// theArray is a valid managed object while the native array is already freed

Edit

From Memory Management I gathered that Marshal.FreeCoTaskMem would most likely be implemented using free(), so the fitting allocator would be malloc().

There are two ways to be really sure:

  1. Allocate the memory in CLI using Marshal.AllocCoTaskMem, pass it to native to have it filled, and then free it in the CLI again using Marshal.FreeCoTaskMem.
  2. Leave it as it is (native allocates memory with malloc()), but do not free the memory in CLI. Instead, have another native function like freeArray(double **data) and have it free() the array for you once CLI is done using it.
like image 172
Thomas Hilbert Avatar answered Oct 22 '22 14:10

Thomas Hilbert


I am not an expert on Unity, but it seems that Unity relies on Mono for it's C# scripting support. Take a look at this documentation page:

Memory Management in Mono

We can assume from there that you will need to have platform-dependent code on your C++ side, you will need to use CoTaskMemAlloc/CoTaskMemFree in Windows and GLib memory functions g_malloc() and g_free() for Unix (like iOS, Android etc).

If you have control over all your code, C++ and C#, the easiest way to implement this would be to do all the memory allocation/deallocation in the C# script.

Sample code (untested):

//C++ code

extern "C" ABA_API long getArrayLength(){
    return delArray.size();
}

extern "C" ABA_API void getArray(long len, double *data){
    if (delArray.size() <= len)
        memcpy(data, delArray.data(), delArray.size());
}

// C# code
[DllImport("AudioPluginSpecDelay")]
private static extern int getArrayLength(); 

[DllImport("AudioPluginSpecDelay")]
private static extern void getArray(int length,[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] double[] array);

int theSize = getArrayLength();
double[] theArray = new double[theSize];
getArray(theSize, theArray);
like image 30
yms Avatar answered Oct 22 '22 14:10

yms