【问题标题】:How to byte array received from socket, to C# structure如何将从套接字接收的字节数组转换为 C# 结构
【发布时间】:2012-09-28 07:03:54
【问题描述】:

我必须开发一个服务 (C#),它通过 TCP 套接字从网络设备读取数据并将其转换为 C# 结构。

我基于现有的旧 Delphi 应用程序,它正在做所有这些事情,我必须在 C# 中迁移逻辑。

编辑: 我从原始数据结构的 C-Source 获得了快照:

struct _RequestMsgStruct
{
    UCHAR           request_ver; //In DELPHI it is represented as Byte
    USHORT          mac_addr[3];    /* MAC Address */
    UINT            product_type; //In DELPHI - Cardinal
    UCHAR           supply_type; //In DELPHI - Byte
    short           reserved0; //In DELPHI - SmallInt
    UCHAR           oper_ver[4]; //In DELPHI - CARDINAL !!!
    USHORT          brd_id; //In DELPHI - WORD
    unsigned short  exp_id1; //In DELPHI - WORD

    //In DELPHI - string[15]; //Array [0..15] of char;
    UCHAR           serial_no[16]; /* Serial Number. 16th char have to be NULL */ 
    UCHAR           _name[32]; /* Name */ //Length of payload may vary //In DELPHI - string[31]

    float           data_avg; //In DELPHI - Single
    ULONG           key[5]; //In DELPHI - array [0..19] of Byte
}__attribute__ ((packed));

Delphi Packed 记录包含 200 多个不同类型的字段...看起来大致如下:

  TREC_DATA = packed record
      ID            : Byte;
      MAC_ADDRESS   : array [0..5] of Byte;
      fieldCard     : cardinal;
      fieldSI       : SmallInt; 
      fieldW        : WORD;
      SERIAL_NUMBER : string[15]; //Array [0..15] of char;
      fieldSingle   : Single;
      fieldArrOfB   : array [0..19] of Byte;
    end;

在 Delphi 中将字节数组移动到结构中,下面的代码:

Move(inBytesArr[StartIdx], DelphiStruct, aMsgSize)

要转换字符串文件(例如SERIAL_NUMBER)也有这样的代码:

var
  pc: Pchar;
...
pc := @inBytesArr[StartIdx + SerialN_Pos_Idx];
DelphiStruct.SERIAL_NUMBER := pc;

我第一次处理这种转换,我不知道从哪里开始:

  • 如何将此结构转换为 c#? -- 我应该使用LayoutKind.SequentialLayoutKind.Explicit,还是使用[FieldOffset(N)] 属性? -- 如何在目标 c# 结构中声明字节数组:作为fixed 缓冲区还是使用[MarshalAs(UnmanagedType.ByValArray...)] 属性?

  • 将输入字节数组编组为最终 C# 结构的更好方法是:使用 Marshal.PtrToStructure 或 GCHandle.Alloc(bytes, GCHandleType.Pinned) + AddrOfPinnedObject

请至少帮助我了解我需要从哪里开始。

【问题讨论】:

  • 我认为你给了我们错误的结构声明。您需要给我们一个与电线上的内容相匹配的那个。我猜那是在C程序中。这些对 Delphi string[] 类型的引用让我们感到困惑。
  • @DavidHeffernan,我得到了原始 C 结构的快照。已更新相关问题。
  • 你可以接受丹尼斯的回答
  • 顺便说一句.. Dennis 的解决方案有效!谢谢
  • 是的。你为什么不接受他的回答?

标签: c# .net delphi


【解决方案1】:

默认情况下,Delphi 的打包记录按单字节边界对齐字段。
因此,你应该使用这样的东西:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct TREC_DATA
{
      public byte ID;
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
      public byte[] MAC_ADDRESS;
      public uint fieldCard;
      public short fieldSI;
      public ushort fieldW;
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
      public byte[] SERIAL_NUMBER;
      public float fieldSingle;
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
      public byte[] fieldArrOfB;    
} 

我唯一不确定(并且现在没有 Deplhi 无法测试)是 SERIAL_NUMBER 字段。

更新后:在原始版本中,SERIAL_NUMBER 只是一个以 null 结尾的字符串。

【讨论】:

  • 没有办法让 pinvoke marshaller 处理字符串[15]
  • @DavidHeffernan,丹尼斯,感谢您的回复 - 我会检查他们。我想在后台指定问题:传入数据是来自网络设备的字节数组(通过 TCP 套接字),设备固件是 UNIX 下的嵌入式 C,我没有相应的来源。我拥有的是旧的大型 Delphi 应用程序,我想将设备通信移动/迁移到新的 C# 服务,因此我正在查看实际的 Delphi 代码,其中我有一些简单的内存复制:Move(inBytesArr[inIdx], DelphiStruct, aMsgSize)。我假设 Delphi string 在传入数据中已经存在 0 前缀字节。
  • @ALZ:这看起来很奇怪......我不相信,一些C编写的软件只为Delphi应用程序构建数据包。也许,有这样的东西(在 C 代码中):byte stringLength; byte[15] serialNumber;?无论如何,我倾向于认为 David 是对的,您应该将此成员编组为字节数组。
  • 你为什么要等到现在才告诉我们?!你的问题看起来像 p/invoke!请编辑问题以包含此信息。
  • @ALZ:看起来像通常的 C 空终止字符串,后来被转换为 Delphi 短字符串。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-17
  • 1970-01-01
  • 2013-09-07
  • 1970-01-01
相关资源
最近更新 更多