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?
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.
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 *= (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 = (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 (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(), length12); // Vector3f has 34 = 12 bytes
gcRes.Free();
I have seen people using the FMOD C# wrapper in Unity, that uses the 'IntPtr' type for pointers.
Is it possible to use Memory Mapping to interact with C++ application in Windows?
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~