1

I have some issues with marshalling script giving an exception. I have C++ structs that I try to mimic in C# for testing a system we have. The C++ struct looks like this:

#pragma pack(1)
typedef struct
{
  ACE_UINT32 result;
  ACE_UINT32 command;
  ACE_TCHAR information[2001];
  ACE_UINT16 informationLength; ///< Length of the variable information.
} MsgStructType;
#pragma pack()

in C# I declare the struct as follows:

[StructLayout(LayoutKind.Explicit, Pack = 1)]
struct MsgStruct
{
  [FieldOffset(0)]
  public uint result;
  [FieldOffset(4)]
  public uint command;
  [FieldOffset(8)]
  public Byte[] information;
  [FieldOffset(2009)]
  public ushort informationLength;
}

I use the following methods to serialize and deserialize the message.

public static T DeserializeMsg<T>(Byte[] data) where T : struct
{
  int objsize = Marshal.SizeOf(typeof(T));
  IntPtr buff = Marshal.AllocHGlobal(objsize);

  Marshal.Copy(data, 0, buff, objsize);

  T retStruct = (T)Marshal.PtrToStructure(buff, typeof(T));

  Marshal.FreeHGlobal(buff);

  return retStruct;
}

public static Byte[] SerializeMessage<T>(T msg) where T : struct
{
  int objsize = Marshal.SizeOf(typeof(T));
  Byte[] ret = new Byte[objsize];

  IntPtr buff = Marshal.AllocHGlobal(objsize);

  Marshal.StructureToPtr(msg, buff, true);

  Marshal.Copy(buff, ret, 0, objsize);

  Marshal.FreeHGlobal(buff);

  return ret;
}

I manage to serialize the message, send it on udp to the same application as received and the size of data seems to be correct. Problem I get is when I try to Deserialize the message. I get the following error code:

Catch exception

Perhaps the method to use Byte[] is incomplete but it is the exact same class I use for serializeing and unserializeing the data. Only difference is that I go throught udp between.

Some trial and error made me come to the insight that the definition of byte[] or char[] seems to be the issue.

[StructLayout(LayoutKind.Explicit, Pack = 1)]
struct MsgStruct
{
  [FieldOffset(0)]
  public uint result;
  [FieldOffset(4)]
  public uint command;
  [FieldOffset(8)]
//  public Byte[] information;
//  [FieldOffset(2009)]
  public ushort informationLength;
}

This one can be transferred without problems between the systems. So I guess its the byte / char array I need to work on to declare correct.

svick
  • 236,525
  • 50
  • 385
  • 514
Andreas
  • 539
  • 1
  • 5
  • 13

2 Answers2

3

If you write just public Byte[] information;, it's marshaled as if it was a pointer to an array, but that's not what you have here. What you need to do is to specify [MarshalAs(UnmanagedType.ByValArray)], so that it's interpreted as an array that's directly in the structure:

[FieldOffset(8)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2001)]
public byte[] information;

And if you do that, I think you won't need LayoutKind.Explicit anymore:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct MsgStruct
{
    public uint result;
    public uint command;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2001)]
    public byte[] information;
    public ushort informationLength;
}

EDIT: The information array has to be exactly the declared length, even you want to put in less than that much bytes. So, with your example, it could look like this:

var msg = new MsgStruct();
msg.information = new byte[2001];
var information = Encoding.ASCII.GetBytes("Kalle");
Array.Copy(information, msg.information, information.Length);
var bytes = SerializeMessage(msg);
svick
  • 236,525
  • 50
  • 385
  • 514
  • Hmm trying this will make the serializemessage throw an unrecognized struct exception on line _Marshal.StructureToPtr(msg, buff, true);_ – Andreas Mar 13 '12 at 05:47
  • It doesn't throw for me. I have no idea what could be the problem. – svick Mar 13 '12 at 09:14
  • How do you assign data to the information table? I used _struct.information = Encoding.ASCII.GetBytes("Kalle");_ when I tried. – Andreas Mar 13 '12 at 12:03
  • That throws an exception complaining about the array length. See edit to my answer to see how to do that correctly. But since your exception message is different, maybe you have some other problem. – svick Mar 13 '12 at 12:16
  • Works for me when declaring new before assigning data. Seems i missed that part. Thanks alot. – Andreas Mar 13 '12 at 12:41
1
  [FieldOffset(8)]
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2001)]
  public Byte[] information;
Serj-Tm
  • 16,581
  • 4
  • 54
  • 61
  • Just a piece of code is not very useful. Next time, you might want to include some information on why is it needed and what it does. – svick Mar 12 '12 at 13:21