Marshalling an Array of Structs between a .dll and Unity

I’ve seen similar questions, but not exactly the same.

I’ve got a plugin that has an array of structs, something like this:

//C struct
typedef struct 
{
  unsigned long var1;
  unsigned long var2;
  short var3;
  unsigned char header[4];
  unsigned char data[8];
} message;

I’d like to get an array of these back to Unity for use in my C# script. I’d like to do it by sending a block of memory to the dll, having it get filled, and then marshalling it to a C# struct. My code looks something like this:

//C++, what goes on in the dll
export "C"
{
  int export_api GetNumMessages()
  {
     //obj is an instance of a class that contains the 
     //messages and knows how many there are.
     return obj->numMessages;
  }

  void export_api GetMessages(message *theMessages)
  {
     //theMessages is supposed to be an array big enough to
     //hold all of them. In Unity, I call GetNumMessages and 
     //allocate it.
     copy_n(obj->theMessages, obj->numMessages, theMessages);
  }
}

Now, in C#, I have something like this:

[ StructLayout( LayoutKind.Sequential, Size=22, CharSet=CharSet.Ansi ), Serializable]
public class Message
{
  public UInt32 var1;
  public UInt32 var2;
  public UInt16 var3;
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  public Byte[] header = new Byte[4];
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
  public Byte[] data = new Buyte[8];
}

Message[] theMessages;

[DllImport("MyPlugin")]
static extern int GetNumMessages();

[DllImport("MyPlugin")]
static extern void GetMessages([Out] out IntPtr messages);

Message[] GetMessagesFromPlugin()
{
  int numMessages = GetNumMessages(); //This is working for me.

  //Allocate a managed array big enough to hold everything
  var dllMessages = new Message[numMessages];
  for(int i = 0; i < numMessages; ++i)
  {
    dllMessages *= new dllMessage();*

}

//Allocate unmanaged array big enough for everything.
var msgType = typeof(Message);
var msgSize = Marshal.SizeOf(msgType);
var dllBytes = Marshal.AllocHGlobal(numMessages * msgSize);

GetMessages(out dllBytes);

var currentMsg = dllBytes;
for(int i = 0; i < numMessages; ++i)
{
dllMessages = (Message)Marshal.PtrToStructure(currentMsg, msgType);
currentMsg = new IntPtr(currentMsg.ToInt64() + msgSize);
}
//Free unmanaged memory
Marshal.FreeHGlobal(dllBytes);

return dllMessages;
}

For those of you still awake, this doesn’t work. The crash report simply says something like “Unhandled exception”.
Any help would be appreciated.

Is there any particular reason for which you align the memory by 22 bytes? You could probably go better with 4 or 8 byte alignment scheme. Also, force the chosen alignment in the C++ declaration.

If you don’t want to alter indefinitely the existing alignment, you can do something like this:

#pragma pack(push)
#pragma pack(4) // or 8

struct my_struct
{
   ...
};

#pragma pack(pop)