问题
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.
回答1:
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:
- Allocate the memory in CLI using
Marshal.AllocCoTaskMem
, pass it to native to have it filled, and then free it in the CLI again usingMarshal.FreeCoTaskMem
. - Leave it as it is (native allocates memory with
malloc()
), but do not free the memory in CLI. Instead, have another native function likefreeArray(double **data)
and have itfree()
the array for you once CLI is done using it.
回答2:
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);
来源:https://stackoverflow.com/questions/36224120/array-from-c-to-c-sharp