I created a cross-platform DLL in C++ that compiles on both Windows and Mac OSX. On Windows, I have a C# app that calls the DLL using P/Invoke and on Mac OSX, an objective C app calls the DLL. I have simple functions working just fine but I need a new function that returns an array of integers.
The best example I can find is at Marshal C++ int array to C# and I was able to make it work. However, I would like to modify this example to pass the integer array back as a reference argument instead. The size of the array has to be set at runtime.
Here's what I've tried. The pSize is coming back correctly but the list is empty.
In unmanaged c++:
bool GetList(__int32* list, __int32* pSize)
{
// Some dummy data
vector<int> ret;
ret.push_back(5);
ret.push_back(6);
list = (__int32*)malloc(ret.size());
for (unsigned int i = 0; i < ret.size(); i++)
{
list[i] = ret.at(i);
}
*pSize = ret.size();
return true;
}
In C#:
[DllImport(@"MyDll.dll",
CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern bool GetList(out IntPtr arrayPtr, out int size);
public static int[] GetList() {
IntPtr arrayValue = IntPtr.Zero;
int size = 0;
bool b = GetFrames(out arrayValue, out size);
// arrayValue is 0 here
int[] result = new int[size];
Marshal.Copy(arrayValue, result, 0, size);
return result;
}
Your problem is the definition of list
, it really needs to be an __int32**
in order to pass back the address of the allocated array. To breeze through the interop difficulties of pointers-to-pointers, how about you instead return the address of list
or null
if it fails:
__int32* GetList(__int32* pSize)
{
// Some dummy data
vector<int> ret;
ret.push_back(5);
ret.push_back(6);
// per @David's catch, you'll need to allocate the right amount
__int32* list = (__int32*)malloc(ret.size() * sizeof(__int32));
for (unsigned int i = 0; i < ret.size(); i++)
{
list[i] = ret.at(i);
}
*pSize = ret.size();
return list;
}
void RemoveList(__int32* list)
{
free(list);
}
With the appropriate modifications to your C#:
[DllImport(@"MyDll.dll",
CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GetList(out int size);
[DllImport(@"MyDll.dll",
CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
private static extern void RemoveList(IntPtr array);
public static int[] GetList()
{
int[] result = null;
int size;
IntPtr arrayValue = IntPtr.Zero;
try
{
arrayValue = GetList(out size);
if (arrayValue != IntPtr.Zero)
{
result = new int[size];
Marshal.Copy(arrayValue, result, 0, size);
}
}
finally
{
// don't forget to free the list
RemoveList(arrayValue);
}
return result;
}