Can't order array of strings from C ++ to C # in Unity

I am trying to pass an array from struct

from C ++ to Unity script in C #. When I use the code in production, the size of the array will vary greatly, so I really need to pass an array of unknown length.

My smart idea was to store the array on the heap and pass a reference to that array to Unity. I found StackOverflow posts on how to do this. But then Unity complains that the link is null.

Here is a portion of my C ++ code:

extern "C" struct S; // Not currently used.

struct S {
  int i;
  float f;
};

extern "C" bool helloWorld(S ** a, int * i);

S * s;

bool helloWorld(S ** a, int * i) {
  s = new S[4];
  for (int j = 0; j < 4; j++) {
    s[j].i = j;           // Fill in placeholder
    s[j].f = (float) j;   // data for now.
  }
  *a = s; // I don't know if this works.
  *i = 4; // Works.
  return true;
}

      

I tried this with int

instead struct

and it worked.

Now my C # code:

[StructLayout(LayoutKind.Sequential),Serializable]
public struct S {
  int i;
  float f;
};

void Start() {
  IntPtr ptrNativeData = IntPtr.Zero;
  int itemsLength = 0;

  bool success = helloWorld(ref ptrNativeData, ref itemsLength);
  if (!success) {
    return;
  }

  S[] SArray = new S[itemsLength];  // Where the final data will be stored.
  IntPtr[] SPointers = new IntPtr[itemsLength];

  Debug.Log("Length: " + itemsLength); // Works!
  Marshal.Copy(ptrNativeData, SPointers, 0, itemsLength); // Seems not to work.

  for (int i = 0; i < itemsLength; i++) {
    Debug.Log("Pointer: " + SPointers[i]); // SPointers[0] prints 0.
    Marshal.PtrToStructure(SPointers[i], SArray[i]); // Crashes here. Boom.
  }
}

[DllImport("TestUnity")]
private static extern bool helloWorld(ref IntPtr ptrResultVerts,
    ref int resultVertLength);

      

The statement Marshal.PtrToStructure

states that the SPointers [i] argument is null. I checked with the Debug.Log command and indeed it looks like null: it prints as 0.

But I've tried something similar with an array before int

and it worked. I'm not sure about this: is this my problem in C ++ or C #? Am I not transmitting correct information or am I not processing correct information in the wrong way?

Solution 1

This was the first solution I came up with mostly on my own. The second solution is better.

Thanks completely to Alex Skalozub, I figured it out. One level of pointer directionality is abstracted during sorting. So my C ++ code contains:

S ** s; // Not a pointer to Ss, but a pointer to pointers to Ss.

bool helloWorld(S *** a, int * i) { // Pass a triple pointer.
  s = new S * [4]; // Create array of pointers.
  for (int j = 0; j < 4; j++) {
    s[j] = new S; // Actually create each object.
    s[j]->i = j;
    s[j]->f = (float) j;
  }
  *a = s;
  *i = 4;
  return true;
}

      

And my C # code contains:

for (int i = 0; i < itemsLength; i++) {
  Debug.Log("Pointer: " + SPointers[i]);
  SArray[i] = (S) Marshal.PtrToStructure(SPointers[i], typeof(S));
}

      

And this! All data has been transferred.

Now this leaves me with a memory management problem: every single object created in C ++ code must be freed . I know about this, and I will take care of it further.

Solution 2

See verified answer. Alex's answer is much better as it uses much less unnecessary pointers. I used more pointers because I just figured out how to use them in C #.

+3


source to share


2 answers


Yours ptrNativeData

is a pointer to an array of the structures themselves, not an array of pointers to structures. Copying it into an array of pointers and accessing them is wrong.

I also suggest using interop out

instead ref

when declaring a function. This is more accurate and does not require marshaller to copy the initial values ​​from managed to native code (since they are initialized in native code):



[DllImport("TestUnity")]
private static extern bool helloWorld(out IntPtr ptrResultVerts, out int resultVertLength);

void Start() {
    IntPtr ptrNativeData;
    int itemsLength;

    bool success = helloWorld(out ptrNativeData, out itemsLength);
    if (!success) {
        return;
    }

    S[] SArray = new S[itemsLength];  // Where the final data will be stored.

    Debug.Log("Length: " + itemsLength);

    IntPtr p = ptrNativeData;
    for (int i = 0; i < itemsLength; i++) {
        Marshal.PtrToStructure(p, SArray[i]);
        p += Marshal.SizeOf(typeof(S)); // move to next structure
    }

    // todo: free ptrNativeData later
}

      

+3


source


Years later, I find this answer and it helps me a little. Although I have something to add, which is easier and better to use. You don't need to worry about freeing memory in C ++ Part:

C ++

#pragma pack(push, 1)

struct Joint
{
    int id = 0;
    float posX = 0.0f;
    float posY = 0.0f;
    float posZ = 0.0f;
};

#pragma pack(pop)

extern "C" void __declspec(dllexport) __stdcall GetJointArray(Joint* newJoints, int* length)
{
    Joint theNewJoints[2];
    Joint theJoint;
    theJoint.id = 20;
    theJoint.posX = 21;
    theJoint.posY = 22;
    theJoint.posZ = 23;
    theNewJoints[0] = theJoint;
    theNewJoints[1] = theJoint;
    memcpy(newJoints, &theNewJoints, 2 * sizeof (theJoint));
    *length = 2;
}

      



FROM#

[StructLayout(LayoutKind.Sequential, Pack = 0)]
internal unsafe struct Joint
{
    [MarshalAs(UnmanagedType.I4)]
    public int id;
    [MarshalAs(UnmanagedType.R4)]
    public float posX;
    [MarshalAs(UnmanagedType.R4)]
    public float posY;
    [MarshalAs(UnmanagedType.R4)]
    public float posZ;
}

[DllImport("TheDLL")]
static extern void GetJointArray(Joint[] jointArray, out int length);

int itemsLength = 0;
Joint[] result = new Joint[2]; // Of course you need an additional dll function call to get the size of the array before
GetJointArray( result, out itemsLength);
Debug.Log("ITEMS LENGTH:" + itemsLength);
Debug.Log("JOINT1 ID:" + result[0].id);

      

One more thing worth mentioning: you need two calls with this approach ... first get the size of the array from the C ++ DLL, allocate the array in C # and let the C ++ DLL fill the array allocated in C # extra call

+1


source







All Articles