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.

4
  • Sorry, it's from the ACE framework, typedef uint32_t ACE_UINT32; typedef char ACE_TCHAR; typedef uint16_t ACE_UINT16 Commented Mar 12, 2012 at 10:07
  • My C++ is outdated, did you check that sizeof(char) == 1 in C++ ? Commented Mar 12, 2012 at 10:33
  • Maybe it's public char[] information; [FieldOffset(4010)] Commented Mar 12, 2012 at 10:35
  • Yep it's 1 byte in size and changing it to char[] does not work. Commented Mar 12, 2012 at 10:49

2 Answers 2

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);
Sign up to request clarification or add additional context in comments.

5 Comments

Hmm trying this will make the serializemessage throw an unrecognized struct exception on line Marshal.StructureToPtr(msg, buff, true);
It doesn't throw for me. I have no idea what could be the problem.
How do you assign data to the information table? I used struct.information = Encoding.ASCII.GetBytes("Kalle"); when I tried.
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.
Works for me when declaring new before assigning data. Seems i missed that part. Thanks alot.
1
  [FieldOffset(8)]
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2001)]
  public Byte[] information;

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.