1

I want to use my laptop to communicate with MES(Manufacturing Execution System). And when I serialized the data (struct type), something happen. The code below is what I have done:

[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct DataPackage
{
    public int a;
    public ushort b;
    public byte c;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)] public string d;
}
class Program
{
    static void Main(string[] args)
    {
        DataPackage pack1 = new DataPackage();
        pack1.a = 0x33333301;
        pack1.b = 200;
        pack1.c = 21;
        pack1.d = "hello";
        byte[] pack1_serialized = getBytes(pack1);
        Console.WriteLine(BitConverter.ToString(pack1_serialized));


        byte[] getBytes(DataPackage str)
        {
            int size = Marshal.SizeOf(str);
            byte[] arr = new byte[size];

            IntPtr ptr = Marshal.AllocHGlobal(size);
            Marshal.StructureToPtr(str, ptr, true);
            Marshal.Copy(ptr, arr, 0, size);
            Marshal.FreeHGlobal(ptr);
            return arr;
        }
    }
}

And here is the outcome: enter image description here

I want the outcome to be like this:

33-33-33-01-00-C8-15-68-65-6C-6C-6F

So the questions are:

  1. Why is the uint / ushort type data reverse after Marshalling?
  2. Is there any other way that I can send the data in the sequence that I want ?
  3. Why is the last word "o" in string "hello" disappear in the byte array ?

Thanks.

元哼黃
  • 87
  • 1
  • 4

1 Answers1

2

1 - Because your expected outcome is big endian, and your system appears to use little endian, so basically reversed order of bytes compared to what you expect.

2- Easiest way is to "convert" your numbers to big endian before marshalling (that is change them in a way which will produce desired result while converting them using little endian), for example like this:

 static int ToBigEndianInt(int x) {
    if (!BitConverter.IsLittleEndian)
        return x; // already fine
    var ar = BitConverter.GetBytes(x);
    Array.Reverse(ar);
    return BitConverter.ToInt32(ar, 0);
 }

static ushort ToBigEndianShort(ushort x) {
    if (!BitConverter.IsLittleEndian)
        return x; // already fine
    var ar = BitConverter.GetBytes(x);
    Array.Reverse(ar);
    return BitConverter.ToUInt16(ar, 0);
}

And then:

pack1.a = ToBigEndianInt(0x33333301);
pack1.b = ToBigEndianShort(200);

Note that this way of conversion is not very efficient and if you need more perfomance you can do this with some bit manipulations.

3 - Because string is null terminated, and this null terminator counts in SizeConst. Since you have it 5, there will be 4 characters of your string + 1 null terminator. Just increase SizeConst = 6 (that might add additional zeroes at the end because of Pack = 4).

Evk
  • 98,527
  • 8
  • 141
  • 191
  • Thanks, really helpful ! And I found out that the MES I want to communicate with use little endian for some attributes in struct and big endian for others. I will try your solution first, and try to control every bits directily if the performance can't feet my need lol . – 元哼黃 Nov 04 '20 at 14:23