- Home /
How do I Pass Arrays from C++ to C# in Unity, if at all Possible?
I don't think that Unity will allow me to use an unsafe context for my C# code, which makes it hard for me to pass pointers back to my C# code. Is there a way to pass array values from C++ code back to C# in Unity?
Without the unsafe keyword you may aswell stop right there, you will not get permissions to use pointers in C# otherwise. It's there to shield pointer usage, that's it's only purpose. So you need a work-a-round in any case.
Is it possible to simply reconstruct the objects with the known data? C# Already throws everything by reference so how does it react if you simply replace the pointer reference to the variable names?
Answer by SteveFSP · Dec 02, 2010 at 03:31 AM
Hmm... How detailed should I get here?
It is possible with one caveat. Assuming you don't use an unsupported hack method you'll need to use Unity Pro's plugin feature.
Here is a quick example of getting a new array of unknown size back from a C++ plugin.
There are various ways of doing it. All involve marshaling across the C#/C++ interop boundary. See also Marshaling Different Types of Arrays.
Some detailed source code examples can be found in a Google code repository here. (Under the NMGen project.) There are examples for the C++ side and the marshaling on the Unity side. Search for "NavmeshGenerator.cs" for the Unity stuff.
// C++ signature. extern "C" EXPORT_API bool getSomeArrayData( float** resultVerts // An array of vertices. , int* resultVertLength); // The length of the returned array.
// The interop signature in the Unity script. [DllImport("mypluginname")] private static extern bool getSomeArrayData(ref IntPtr ptrResultVerts , ref int resultVertLength);
// An example of calling the interop function. public MyMarshallingMethod() { IntPtr ptrResultVerts = IntPtr.Zero; int resultVertLength = 0;
bool success = getSomeArrayData(ref ptrResultVerts, ref resultVertLength)
float[] resultVertices = null;
if (success)
{
// Load the results into a managed array.
resultVertices = new float[resultVertLength];
Marshal.Copy(ptrResultVerts
, resultVertices
, 0
, resultVertLength);
/*
* WARNING!!!! IMPORTANT!!!
* In this example the plugin created an array allocated
* in unmanged memory. The plugin will need to provide a
* means to free the memory.
*/
}
// Do something with the array results.
}
Updates based on the comments:
In the above example, it is assumed that the array is created and managed by the plugin, which tends to limit your options. Various other options are available if you can create and manage the array on the C# side, though they depend on you knowing the size of the array first.
It is possible to allocate the memory behind an IntPtr in C#, pass it to the plugin in a manner similar to the above example, and access/update its memory using the various IntPtr related methods in the Marshal class. In this case the C# code will also need to handle the manual de-allocation. I tend to use this method only when almost all access is on the plugin side and I only do occasional batch modifications on the C# side.
If you are doing a lot of access on the C# side you can pass a managed array directly.
// C++ signature. extern "C" EXPORT_API bool fillSomeArrayData( float* resultVerts // An array of vertices. , int* resultVertCount // The number of vertices returned in the array. , int resultVertsSize); // The length of the array.
// The interop signature in the Unity script. [DllImport("mypluginname")] private static extern bool fillSomeArrayData([In, Out] float[] resultVerts , ref int resultVertCount , int resultVertsSize);
The [In, Out] attribute will give a hint to the .NET compiler to pin the memory if it thinks best.
Thanks for these details, that spawn 3 questions:
is it possible to $$anonymous$$arshal.Copy into an existing array (i.e. not doing resultVertices = null)?
How would you go about passing an existing C# array to a C plugin with read/write access?
Can this awful copying overhead be avoided by pinning down the float or int array element memory and passing a pointer to C?
Yes, there is no need to create a new array each time, as long as you have one around that is large enough to hold the result of the copy.
See the updated answer. I actually prefer the managed array method to the IntPtr method when it is possible.
If you are using the IntPtr method, the data is already in unmanaged memory, so pinning does not apply. The answer to #2 shouldn't require pinning either. (It should be handled automatically.)
Answer by tteneder · Jan 13, 2016 at 02:30 PM
Based on the previous answers I got something similar.
My personal requirements differ a bit: - transfer call initiated by C++ - one call only - I need arrays of structs as well
C++:
struct Vector3f {
float x;
float y;
float z;
} Vector3;
typedef void (*CB)( void*, int );
static CB cb;
void Init( CB fp ) {
cb=fp;
}
void triggerArrayCopy() {
struct Vector3f x[] = {
{0, 1, 2},
{10, 20, 30},
{100, 200, 300},
};
if(cb) {
cb( (void*)x, 3 );
}
}
C#:
private delegate void myDelegate( IntPtr array, int length);
private static T[] GetNativeArray<T>(IntPtr array, int length) {
T[] result = new T[length];
int size = Marshal.SizeOf (typeof(T));
if (IntPtr.Size == 4) {
// 32-bit system
for (int i = 0; i < result.Length; i++) {
result [i] = (T)Marshal.PtrToStructure (array, typeof(T));
array = new IntPtr (array.ToInt32 () + size);
}
} else {
// probably 64-bit system
for (int i = 0; i < result.Length; i++) {
result [i] = (T)Marshal.PtrToStructure (array, typeof(T));
array = new IntPtr (array.ToInt64 () + size);
}
}
return result;
}
[MonoPInvokeCallback (typeof (myDelegate))]
protected static void CallBack( IntPtr array, int length) {
Vector3[] resultVertices = GetNativeArray<Vector3> (array, length);
if (resultVertices != null) {
foreach (Vector3 f in resultVertices) {
Debug.Log (f);
}
}
}
// Use this for initialization
void Start () {
Init (CallBack);
triggerArrayCopy ();
}
EDIT: In the meantime i figured Iterating over the array and using Marshal.PtrToStructure is slow and creates more garbage memory. A better alternative is something like this:
var gcRes = GCHandle.Alloc(result, GCHandleType.Pinned);
Marshal.Copy(array, 0, gcRes.AddrOfPinnedObject(), length*12); // Vector3f has 3*4 = 12 bytes
gcRes.Free();
Answer by _Petroz · Dec 02, 2010 at 12:01 AM
I have seen people using the FMOD C# wrapper in Unity, that uses the 'IntPtr' type for pointers.
Answer by dwbool · Oct 13, 2012 at 09:44 PM
Is it possible to use Memory Mapping to interact with C++ application in Windows?
Answer by mynameisjun · May 11, 2018 at 06:27 AM
just have a look at my working code. it's passing (float array of C++) ---> (IntPtr of C# of Unity)
C++
__declspec(dllexport) float* GetDeviceLocalPos() // float* can be IntPtr in c#
{
float* position = new float[3];
hdGetFloatv(HD_CURRENT_POSITION, position);
return position;
}
Convert Function in C#
public static float[] IntPtrToFloat3(IntPtr ptr)
{
float[] posFloat3 = new float[3];
IntPtr srcPtr = ptr;
Marshal.Copy(srcPtr, posFloat3, 0, 3);
return posFloat3;
}
And NEVER forget to check the comments below~
Correct me if I'm wrong, but this creates a memory hole, since 'position' is never deleted on C++ side, right?
Absolutely right. Bad example. You either need to track the objects on the C++ side and free them at appropriate times, or you have to track the native pointer on the C# side and pass it to a dedicated "free" method in your native code. That's why it's generally simpler to use a managed array and just pass a reference to the native code.
yes you guys are right unity crashed poping up a window saying 'System out of memory' so Solution would be in my assumption freeing that float array memory in C++. but as you comments about using a $$anonymous$$ANAGED ARRAY which i have not heard before. could you explain a bit more about what exactly it is and how to use that for more fancy solution??? thanks
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
How do I select all other arrays 1 Answer
App work in play test mode but not working in android real device 0 Answers
Saving 30 bools in an array possible? 2 Answers