C#中封送处理期间的字节序列问题
Bytes sequence problem during Marshaling in C#
我想用我的笔记本电脑与MES(制造执行系统)进行通信。
当我序列化数据(结构类型)时,发生了一些事情。
下面的代码是我所做的:
[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;
}
}
}
结果如下:
我希望结果是这样的:
33-33-33-01-00-C8-15-68-65-6C-6C-6F
所以问题是:
- 为什么uint / ushort类型的数据编组后是反的?
- 有没有其他方法可以按我想要的顺序发送数据?
- 为什么字符串“hello”中的最后一个单词“o”在字节数组中消失了?
谢谢。
1 - 因为您的预期结果是大端,而您的系统似乎使用小端,所以字节顺序与您的预期基本上相反。
2- 最简单的方法是在编组之前将您的数字“转换”为大端(即以产生所需结果的方式更改它们,同时使用小端转换它们),例如:
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);
}
然后:
pack1.a = ToBigEndianInt(0x33333301);
pack1.b = ToBigEndianShort(200);
请注意,这种转换方式效率不高,如果您需要更多性能,可以通过一些位操作来实现。
3 - 因为字符串以 null 终止,并且此 null 终止符计入 SizeConst
。因为你有 5,所以你的字符串将有 4 个字符 + 1 个空终止符。只需增加 SizeConst = 6
(由于 Pack = 4
,可能会在末尾添加额外的零)。
我想用我的笔记本电脑与MES(制造执行系统)进行通信。 当我序列化数据(结构类型)时,发生了一些事情。 下面的代码是我所做的:
[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;
}
}
}
结果如下:
我希望结果是这样的:
33-33-33-01-00-C8-15-68-65-6C-6C-6F
所以问题是:
- 为什么uint / ushort类型的数据编组后是反的?
- 有没有其他方法可以按我想要的顺序发送数据?
- 为什么字符串“hello”中的最后一个单词“o”在字节数组中消失了?
谢谢。
1 - 因为您的预期结果是大端,而您的系统似乎使用小端,所以字节顺序与您的预期基本上相反。
2- 最简单的方法是在编组之前将您的数字“转换”为大端(即以产生所需结果的方式更改它们,同时使用小端转换它们),例如:
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);
}
然后:
pack1.a = ToBigEndianInt(0x33333301);
pack1.b = ToBigEndianShort(200);
请注意,这种转换方式效率不高,如果您需要更多性能,可以通过一些位操作来实现。
3 - 因为字符串以 null 终止,并且此 null 终止符计入 SizeConst
。因为你有 5,所以你的字符串将有 4 个字符 + 1 个空终止符。只需增加 SizeConst = 6
(由于 Pack = 4
,可能会在末尾添加额外的零)。