欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#使用struct直接轉(zhuǎn)換下位機數(shù)據(jù)的示例代碼

 更新時間:2021年01月20日 15:45:22   作者:波多爾斯基  
這篇文章主要介紹了C#使用struct直接轉(zhuǎn)換下位機數(shù)據(jù)的示例代碼,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

編寫上位機與下位機通信的時候,涉及到協(xié)議的轉(zhuǎn)換,比較多會使用到二進制。傳統(tǒng)的方法,是將數(shù)據(jù)整體獲取到byte數(shù)組中,然后逐字節(jié)對數(shù)據(jù)進行解析。這樣操作工作量比較大,對于較長數(shù)據(jù)段更容易計算位置出錯。

其實,對于下位機給出通訊的數(shù)據(jù)結(jié)構(gòu)的情況下,可以直接使用C#的struct將數(shù)據(jù)直接轉(zhuǎn)換。需要使用到Marshal。

數(shù)據(jù)結(jié)構(gòu)

假定下位機(C語言編寫)給到我們的數(shù)據(jù)結(jié)構(gòu)是這個,傳輸方式為小端方式

typedef struct {
	unsigned long int time;   // 4個字節(jié)
	float tmpr[3];     // 4*3 個字節(jié)
	float forces[6];     // 4*6個字節(jié)
	float distance[6];    // 4*6個字節(jié)
} dataItem_t;

方法1

首先需要定義一個struct:

[StructLayout(LayoutKind.Sequential, Size = 64, Pack = 1)]
public struct HardwareData
{
 //[FieldOffset(0)]
 public UInt32 Time;   // 4個字節(jié)
 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
 //[FieldOffset(4)]
 public float[] Tmpr;     // 3* 4個字節(jié)
 //[FieldOffset(16)]
 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
 public float[] Forces;     // 6* 4個字節(jié)
 //[FieldOffset(40)]
 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
 public float[] Distance;    // 6*4個字節(jié)
}

然后使用以下代碼進行轉(zhuǎn)換

// code from https://stackoverflow.com/questions/628843/byte-for-byte-serialization-of-a-struct-in-c-sharp/629120#629120
/// <summary>
/// converts byte[] to struct
/// </summary>
public static T RawDeserialize<T>(byte[] rawData, int position)
{
 int rawsize = Marshal.SizeOf(typeof(T));
 if (rawsize > rawData.Length - position)
  throw new ArgumentException("Not enough data to fill struct. Array length from position: " + (rawData.Length - position) + ", Struct length: " + rawsize);
 IntPtr buffer = Marshal.AllocHGlobal(rawsize);
 Marshal.Copy(rawData, position, buffer, rawsize);
 T retobj = (T)Marshal.PtrToStructure(buffer, typeof(T));
 Marshal.FreeHGlobal(buffer);
 return retobj;
}

/// <summary>
/// converts a struct to byte[]
/// </summary>
public static byte[] RawSerialize(object anything)
{
 int rawSize = Marshal.SizeOf(anything);
 IntPtr buffer = Marshal.AllocHGlobal(rawSize);
 Marshal.StructureToPtr(anything, buffer, false);
 byte[] rawDatas = new byte[rawSize];
 Marshal.Copy(buffer, rawDatas, 0, rawSize);
 Marshal.FreeHGlobal(buffer);
 return rawDatas;
}

注意這里我使用的方式為LayoutKind.Sequential,如果直接使用LayoutKind.Explicit并設(shè)置FieldOffset會彈出一個詭異的錯誤System.TypeLoadException:“Could not load type 'ConsoleApp3.DataItem' from assembly 'ConsoleApp3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 4 that is incorrectly aligned or overlapped by a non-object field.”

方法2

提示是對齊的錯誤,這個和編譯的時候使用的32bit和64位是相關(guān)的,詳細數(shù)據(jù)封送對齊的操作我不就詳細說了,貼下代碼。

//強制指定x86編譯
[StructLayout(LayoutKind.Explicit, Size = 64, Pack = 1)]
public struct DataItem
{
 [MarshalAs(UnmanagedType.U4)]
 [FieldOffset(0)]
 public UInt32 time;   // 4個字節(jié)
 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.R4)]
 [FieldOffset(4)]
 public float[] tmpr;     // 3* 4個字節(jié)
 [FieldOffset(16)]
 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6, ArraySubType = UnmanagedType.R4)]
 public float[] forces;     // 6* 4個字節(jié)
 [FieldOffset(40)]
 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6, ArraySubType = UnmanagedType.R4)]
 public float[] distance;    // 6*4個字節(jié)
}

強制指定x64編譯沒有成功,因為數(shù)據(jù)對齊后和從下位機上來的數(shù)據(jù)長度是不符的。

方法3

微軟不是很推薦使用LayoutKind.Explicit,如果非要用并且不想指定平臺的話,可以使用指針來操作,當然,這個需要unsafe。

var item = RawDeserialize<DataItem>(tail.ToArray(), 0);
unsafe
{
 float* p = &item.forces;
 for (int i = 0; i < 6; i++)
 {
  Console.WriteLine(*p);
  p++;
 }
}

[StructLayout(LayoutKind.Explicit, Size = 64, Pack = 1)]
public struct DataItem
{
 [FieldOffset(0)]
 public UInt32 time;   // 4個字節(jié)
 [FieldOffset(4)]
 public float tmpr;     // 3* 4個字節(jié)
 [FieldOffset(16)]
 public float forces;     // 6* 4個字節(jié)
 [FieldOffset(40)]
 public float distance;    // 6*4個字節(jié)
}

方法4

感覺寫起來還是很麻煩,既然用上了unsafe,就干脆直接一點。

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct DataItem
{
 public UInt32 time;   // 4個字節(jié)
 public fixed float tmpr[3];     // 3* 4個字節(jié)
 public fixed float forces[6];     // 6* 4個字節(jié)
 public fixed float distance[6];    // 6*4個字節(jié)
}

這樣,獲得數(shù)組可以直接正常訪問,不再需要unsafe了。

總結(jié)

數(shù)據(jù)解析作為上下位機通訊的常用操作,使用struct直接轉(zhuǎn)換數(shù)據(jù)可以大大簡化工作量。建議還是使用LayoutKind.Sequential來進行封送數(shù)據(jù),有關(guān)于數(shù)據(jù)在托管與非托管中的轉(zhuǎn)換,可以詳細看看微軟有關(guān)互操作的內(nèi)容。

以上代碼在.NET 5.0下編譯通過并能正常執(zhí)行。

補充

注意上面的前提要求是字節(jié)序為小端字節(jié)序(一般計算機都是小端字節(jié)序),對于大端字節(jié)序發(fā)送過來的數(shù)據(jù),需要進行字節(jié)序轉(zhuǎn)換。我找到一處代碼寫的很好:

//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://stackoverflow.com/questions/628843/byte-for-byte-serialization-of-a-struct-in-c-sharp/629120

https://stackoverflow.com/questions/2871/reading-a-c-c-data-structure-in-c-sharp-from-a-byte-array/41836532

到此這篇關(guān)于C#使用struct直接轉(zhuǎn)換下位機數(shù)據(jù)的文章就介紹到這了,更多相關(guān)C#下位機數(shù)據(jù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C#實現(xiàn)windows系統(tǒng)重啟和關(guān)機的代碼詳解

    C#實現(xiàn)windows系統(tǒng)重啟和關(guān)機的代碼詳解

    這篇文章主要介紹了C#實現(xiàn)windows系統(tǒng)重啟和關(guān)機的的方法,涉及C#調(diào)用windows系統(tǒng)命令實現(xiàn)控制開機、關(guān)機等操作的技巧,非常簡單實用,需要的朋友可以參考下
    2024-02-02
  • C#使用Socket實現(xiàn)本地多人聊天室

    C#使用Socket實現(xiàn)本地多人聊天室

    這篇文章主要為大家詳細介紹了C#使用Socket實現(xiàn)本地多人聊天室,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • C#線程入門教程之單線程介紹

    C#線程入門教程之單線程介紹

    這篇文章主要介紹了C#線程入門教程之單線程介紹,本文講解了什么是進程、什么是線程、什么是多線程等內(nèi)容,并給出了一個單線程代碼示例,需要的朋友可以參考下
    2015-02-02
  • C#圖像處理之圖像目標質(zhì)心檢測的方法

    C#圖像處理之圖像目標質(zhì)心檢測的方法

    這篇文章主要介紹了C#圖像處理之圖像目標質(zhì)心檢測的方法,可實現(xiàn)C#計算圖像質(zhì)心的相關(guān)技巧,需要的朋友可以參考下
    2015-04-04
  • C#搜索文字在文件及文件夾中出現(xiàn)位置的方法

    C#搜索文字在文件及文件夾中出現(xiàn)位置的方法

    這篇文章主要介紹了C#搜索文字在文件及文件夾中出現(xiàn)位置的方法,涉及C#針對文件及文件夾遍歷與查找的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-08-08
  • Unity3D在Preview中打印日志的方法

    Unity3D在Preview中打印日志的方法

    這篇文章主要為大家詳細介紹了Unity3D在Preview中打印日志的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-09-09
  • 使用VS2010 C#開發(fā)ActiveX控件(上)

    使用VS2010 C#開發(fā)ActiveX控件(上)

    最近做讀卡器的B/S應(yīng)用程序開發(fā),由于讀卡器廠商提供的手冊都是C/S版本的,而且只有一個原始的Dll包,并沒有web版的,那么就只好自己動手,豐衣足食了
    2011-05-05
  • C#.NET學(xué)習(xí)筆記5 C#中的條件編譯

    C#.NET學(xué)習(xí)筆記5 C#中的條件編譯

    條件編譯是C#比Java多出的東西,但我跟前輩請教后,他們都說條件編譯在實際的項目開發(fā)中不怎么使用.鑒于是新內(nèi)容,我還是做做筆記,理解一下好了
    2012-11-11
  • C# 并發(fā)控制框架之單線程環(huán)境下實現(xiàn)每秒百萬級調(diào)度

    C# 并發(fā)控制框架之單線程環(huán)境下實現(xiàn)每秒百萬級調(diào)度

    本文介紹了一款專為工業(yè)自動化及機器視覺開發(fā)的C#并發(fā)流程控制框架,通過模仿Go語言并發(fā)模式設(shè)計,支持高頻調(diào)度及復(fù)雜任務(wù)處理,已在多個項目中驗證其穩(wěn)定性和可靠性
    2024-10-10
  • C#判斷字符串不等于空的方法小結(jié)

    C#判斷字符串不等于空的方法小結(jié)

    在C#中,要判斷一個字符串是否不等于空(即它既不是null也不是空字符串""),方法有如下幾種,文中通過代碼示例講解的非常詳細,對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-06-06

最新評論