C#使用struct直接转换下位机数据 (2)

注意上面的前提要求是字节序为小端字节序(一般计算机都是小端字节序),对于大端字节序发送过来的数据,需要进行字节序转换。我找到一处代码写的很好:

//CODE FROM https://stackoverflow.com/a/15020402 public static class FooTest { [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct Foo2 { public byte b1; public short s; public ushort S; public int i; public uint I; public long l; public ulong L; public float f; public double d; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)] public string MyString; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct Foo { public byte b1; public short s; public ushort S; public int i; public uint I; public long l; public ulong L; public float f; public double d; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)] public string MyString; public Foo2 foo2; } public static void test() { Foo2 sample2 = new Foo2() { b1 = 0x01, s = 0x0203, S = 0x0405, i = 0x06070809, I = 0x0a0b0c0d, l = 0xe0f101112131415, L = 0x161718191a1b1c, f = 1.234f, d = 4.56789, MyString = @"123456789", // null terminated => only 9 characters! }; Foo sample = new Foo() { b1 = 0x01, s = 0x0203, S = 0x0405, i = 0x06070809, I = 0x0a0b0c0d, l = 0xe0f101112131415, L = 0x161718191a1b1c, f = 1.234f, d = 4.56789, MyString = @"123456789", // null terminated => only 9 characters! foo2 = sample2, }; var bytes_LE = Dummy.StructToBytes(sample, Endianness.LittleEndian); var restoredLEAsLE = Dummy.BytesToStruct<Foo>(bytes_LE, Endianness.LittleEndian); var restoredLEAsBE = Dummy.BytesToStruct<Foo>(bytes_LE, Endianness.BigEndian); var bytes_BE = Dummy.StructToBytes(sample, Endianness.BigEndian); var restoredBEAsLE = Dummy.BytesToStruct<Foo>(bytes_BE, Endianness.LittleEndian); var restoredBEAsBE = Dummy.BytesToStruct<Foo>(bytes_BE, Endianness.BigEndian); Debug.Assert(sample.Equals(restoredLEAsLE)); Debug.Assert(sample.Equals(restoredBEAsBE)); Debug.Assert(restoredBEAsLE.Equals(restoredLEAsBE)); } public enum Endianness { BigEndian, LittleEndian } private static void MaybeAdjustEndianness(Type type, byte[] data, Endianness endianness, int startOffset = 0) { if ((BitConverter.IsLittleEndian) == (endianness == Endianness.LittleEndian)) { // nothing to change => return return; } foreach (var field in type.GetFields()) { var fieldType = field.FieldType; if (field.IsStatic) // don't process static fields continue; if (fieldType == typeof(string)) // don't swap bytes for strings continue; var offset = Marshal.OffsetOf(type, field.Name).ToInt32(); // handle enums if (fieldType.IsEnum) fieldType = Enum.GetUnderlyingType(fieldType); // check for sub-fields to recurse if necessary var subFields = fieldType.GetFields().Where(subField => subField.IsStatic == false).ToArray(); var effectiveOffset = startOffset + offset; if (subFields.Length == 0) { Array.Reverse(data, effectiveOffset, Marshal.SizeOf(fieldType)); } else { // recurse MaybeAdjustEndianness(fieldType, data, endianness, effectiveOffset); } } } internal static T BytesToStruct<T>(byte[] rawData, Endianness endianness) where T : struct { T result = default(T); MaybeAdjustEndianness(typeof(T), rawData, endianness); GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned); try { IntPtr rawDataPtr = handle.AddrOfPinnedObject(); result = (T)Marshal.PtrToStructure(rawDataPtr, typeof(T)); } finally { handle.Free(); } return result; } internal static byte[] StructToBytes<T>(T data, Endianness endianness) where T : struct { byte[] rawData = new byte[Marshal.SizeOf(data)]; GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned); try { IntPtr rawDataPtr = handle.AddrOfPinnedObject(); Marshal.StructureToPtr(data, rawDataPtr, false); } finally { handle.Free(); } MaybeAdjustEndianness(typeof(T), rawData, endianness); return rawData; } } 参考资料

https://www.developerfusion.com/article/84519/mastering-structs-in-c/

https://stackoverflow.com/a/15020402

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsfpdp.html