The only safe way to have C++ code update a managed C# array is to pin the array. Otherwise, it's possible for the garbage collector to try to move the array while the native code is running. You can do this with a GCHandle object.
int count = 5;
float[] resultVertices = new float[count];
GCHandle handle = GCHandle.Alloc(resultVertices, GCHandleType.Pinned);
IntPtr address = handle.AddrOfPinnedObject();
PopulateArray(address, count);
handle.Free();
It can also be done with unsafe code, which is somewhat more intuitive to read and remember:
int count = 5;
float[] resultVertices = new float[count];
unsafe
{
fixed(float* ptr = resultVertices)
{
PopulateArray(ptr, count);
}
}
Another alternative is to have C# allocate an unmanaged chunk of memory and pass that to the C++ method. This is better than what you're doing because you are not placing the responsibility of allocation/deallocation in the C++ library code and instead keeping that all in your C#. I know you want to avoid the coy but sometimes doing the copy is more performant than pinning objects, but it depends on how large they are. I recommend you do performance testing to determine which is best for your situation.
int count = 5;
float[] resultVertices = new float[count];
IntPtr unmanagedMemory = Marshal.AllocHGlobal(count * Marshal.SizeOf(typeof(float)));
PopulateArray(unmanagedMemory, count);
Marshal.Copy(unmanagedMemory, resultVertices, 0, count);
In all these scenarios you should set your C++ code to operate like this:
extern "C" __declspec(dllexport) bool PopulateArray(float* resultVerts, int vertLength)
{
resultVerts[0] = 0.123f;
// fill out the rest of them any way you like.
return true;
}
If the array size is variable, then I recommend having a separate C++ method that calculates the size and returns it rather than having the C++ method allocate the memory.