9

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.

5
  • are you saying you are not sure what to do on the c++ side, or, on the c# side? where is the problem? Commented Mar 25, 2016 at 17:15
  • Hey, the problem is with CoTaskMemAlloc which allocates the memory for the array (and it HAS to be CoTaskMemAlloc, i tried with other memory allocations and it didn't work). It does not exist on mac! as yms pointed out below GLib is supposedly the alternative for mac. I am open to other methods that do not use GLib :) Commented Mar 25, 2016 at 17:22
  • 1
    You could also do all memory management on the C# side :) Commented Mar 25, 2016 at 17:49
  • hi @k9256 - I wanted to ask you something. Just FWIW, what are you actually trying to do? FYI: it's extremely common with Unity that: experienced programmers (like yourself), who are fooling around with Unity, "accidentally" as it were go down the path of trying to do something, unaware that there is a ridiculously simple way to achieve said thing in the Unity milieu. Just as a curiosity what are you actually trying to do? Cheers Commented Mar 26, 2016 at 15:11
  • Hey Joe, I am currently making a spectral delay audio plugin using the unity native audio SDK. The array that is going between c++/c# are delay time arrays so the user can choose different times for each bin of the FFT(which is in C++) with a GUI. Its my first time using the SDK, and tbh I haven't used C++ a lot. So learning best practices with both at the same time is challenging! Commented Mar 26, 2016 at 16:57

2 Answers 2

8

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.
Sign up to request clarification or add additional context in comments.

6 Comments

This is absolutely perfect, I was getting far too caught up in following the other method perfectly to think about doing this! Thanks.
@k9256 I am not sure this is correct... you will be allocating with one function and de-allocating with a different (possibly unrelated) function. At least try to make sure that your code is not leaking memory, even if it looks like "it works"
Just checked, the memory usage for Unity does creep up when I am using the GUI I have made. I tried adding free(*data) in c++, but this causes a crash. Do you suggestions on where to look to stop memory leakage?
I am now not sure about memory leakage, Unity seems to creep up on its own without my plugin present. Would Marshal.FreeCoTaskMem(arrayPtr) not free the memory allocated by C++? I know that I should be using CoTaskMemAlloc in C++, but I am not entirely clear on the difference
As the first approach was to free the memory in CLI, programming it "properly" - which I understand to mean new and delete - won't work, because they contain extra logic the CLI does not know about. So the solution had to be restricted to plain memory allocation. Feel free to post an answer proposing a different approach.
|
4

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);

1 Comment

Hey, Thanks for the doc link, I am attempting to get GLib now. I am not hugely confident in C++ so I wish I didnt have to, but it seems unavoidable!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.