-1

I have this struct:

[StructLayout(LayoutKind.Explicit)]
private struct Test
{
    [FieldOffset(0)]
    public readonly short Foo;

    [FieldOffset(2)]
    public readonly short Bar;

    [FieldOffset(4)]
    public readonly short Baz;
}

And the following byte array:

var bytes = new byte[]
{
    0x00, 0x01,
    0x00, 0x05,
    0xFF, 0xFB
};

And I convert my byte array to the structure with the following helper function:

private static T ByteArrayToStructure<T>(byte[] bytes)
    where T : struct
{
    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try
    {
        return (T) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    }
    finally
    {
        handle.Free();
    }
}

So I use it as follows:

var test = ByteArrayToStructure<Test>(bytes);

Assert.AreEqual(1, test.Foo);
Assert.AreEqual(5, test.Bar);
Assert.AreEqual(-5, test.Baz);

Now looking at the Asserts we can clearly see what I'm expecting, and this question would be here if the results are not this.

It seems that something is flipping the 2 bytes, so that for the first one it is 0x0100 instead of 0x0001, for the second one it comes up as 0x0500 instead of 0x0005, and for the last one it shows up as 0xFBFF instead of 0xFFFB.

Is there a way to disable this behavior?

halfer
  • 19,824
  • 17
  • 99
  • 186
Anemoia
  • 7,928
  • 7
  • 46
  • 71
  • https://stackoverflow.com/questions/2480116/marshalling-a-big-endian-byte-collection-into-a-struct-in-order-to-pull-out-valu – Sam Axe Dec 26 '17 at 00:27
  • 1
    Your expectations are out of line with reality; consider updating your expectations to match reality and then the mismatch will go away. The behaviour you're seeing is correct. If you want to adjust the endianness then either adjust it on the byte array end, or write a marshaller. – Eric Lippert Dec 26 '17 at 00:30
  • 1
    There is no "flipping", the array has the byte values in the wrong order. Your machine is little-endian, afaik the last one big-endian one that could execute C# code was the xbox-360. So the little end has to go first, 0x01, 0x00, 0x05, etc. Humans tend to prefer big-endian, but they lost the battle with the machine. History [is here](https://stackoverflow.com/a/36263273/17034). – Hans Passant Dec 26 '17 at 00:58
  • If you really want to start with big-endian data, see marked duplicate for how to swap the bytes after marshalling so that you get expected results. If it's just a matter of you not understanding the difference between big- and little-endian and you've just initialized the `byte[]` incorrectly, then...just initialize the `byte[]` array correctly. – Peter Duniho Dec 26 '17 at 01:22

1 Answers1

0

If you want to change the endianness of your data, this would not be enough:

Array.Reverse(data);
Test t = ByteArrayToStructure<Test>(bytes);

since then you would also obtain a reverse order of the struct values:

t.Foo will be -5
t.Bar will be 5
t.Baz will be 1

You can inverse the byte order of your array only if you know the size of each field of the structure, so that you can selectively reverse their respective byte blocks in the array. Alternatively, you should perform a byte swapping on each field of your structure after it has been marshaled from the array, like this:

Test t = ByteArrayToStructure<Test>(bytes);
t.Foo = SwapBytes(t.Foo);
t.Bar = SwapBytes(t.Bar);
t.Baz = SwapBytes(t.Baz);

private static short SwapBytes(short value)
{
    return (short)((value >> 8) | ((value & 0xFF) << 8));
}

but I think the logics of your code are flawed if you need to do this.

I am finding many alternative solutions, like the one that @Sam Axe posted in his commend, or this one... but I still don't think it's a good practice.

Tommaso Belluzzo
  • 23,232
  • 8
  • 74
  • 98