C# Struct 到字节数组的编组方式与 Delphi 中的打包记录相同
Posted
技术标签:
【中文标题】C# Struct 到字节数组的编组方式与 Delphi 中的打包记录相同【英文标题】:C# Struct to byte array marshalling the same way as with packed records in Delphi 【发布时间】:2018-04-19 05:05:12 【问题描述】:我有一个用 Delphi 2007 编写的遗留应用程序,它生成这样的字节数组:
command_data = packed record
direction : word;
name : array [0..9] of char;
end;
command_header = packed record
length : word;
data1 : word;
data2 : cardinal;
end;
command_data_container = packed record
data : command_data;
containerId : word;
end;
function Generate(name: string)boolean;
var
header : command_header;
container : command_data_container;
charArrayName: array [0..9] of char;
begin
charArrayName = array [0..9] of char;
for I := 0 to Length(name) - 1 do begin
charArrayName[i] := name[i+1];
end;
charArrayName[i+1] := #0;
header.length := sizeof(header) + sizeof(container);
header.data1 := 0;
header.data2 := 1;
container.data.direction := 1;
container.data.name := charArrayName;
container.containerId := 1;
stream := TMemoryStream.Create;
stream.WriteBuffer(header, SizeOf(header));
stream.WriteBuffer(container, SizeOf(container));
//...
end;
我需要用 C# 重写这部分。到目前为止我得到了这个:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct command_data
public ushort direction;
public fixed char name[10];
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct command_header
public ushort length;
public ushort data1;
public ulong data2;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct command_data_container
public command_data data;
public ushort containerId;
public bool Generate(string name)
name = name + Char.MinValue; // Add null terminator.
char[] charArrayName = agentId.ToCharArray();
unsafe
command_header header;
command_data_container command;
header.data1 = 0;
header.data2 = 1;
header.length = (ushort)(sizeof(command_header) + sizeof(command_data_container));
command.data.direction = 1;
*command.data.name = charArrayName[0];
for (int i = 1; i < charArrayName.Length; i++)
*(command.data.name + i) = charArrayName[i];
command.containerId = 1;
var headerBytes = StructToBytes<command_header>(header);
var commandBytes = StructToBytes<command_data_container>(command);
byte[] combined = new byte[headerBytes.Length + commandBytes.Length];
Buffer.BlockCopy(headerBytes, 0, combined, 0, headerBytes.Length);
Buffer.BlockCopy(commandBytes, 0, combined, headerBytes.Length, commandBytes.Length);
//combined should have the same data as the stream in the delphi code
public static byte[] StructToBytes<T>(T structure) where T : struct
int size = Marshal.SizeOf(structure);
byte[] rawData = new byte[size];
GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
try
IntPtr rawDataPtr = handle.AddrOfPinnedObject();
Marshal.StructureToPtr(structure, rawDataPtr, false);
finally
handle.Free();
return rawData;
我尝试了几种方法将结构转换为字节数组,但都没有重现与 Delphi 代码相同的结果。
StructToBytes 方法是从这里借用的:C# performance - Using unsafe pointers instead of IntPtr and Marshal
我还尝试了其他一些来自 SO 的编组方法,但没有任何效果。 我做错了什么?
【问题讨论】:
你知道 Delphi 2007 Char 是 AnsiChar 而在 C# 中是 Unicode?span> c# 中的字符是两个字节,而在 c++ 中它们是一个字节。所以在 c# 中使用:new byte[10]。该数组还必须以“\0”(0x00) 终止。我通常使用:Encoding.UTF8.GetBytes("JohnSmith\0"); 如果您必须在 C# 中重写,那么您为什么要关心互操作和编组?只需生成相同数量的字节。不能太难。 我同意@Rudy 的观点。直接写入字节流更简单。显然,这里的大问题是已经确定的 char 周围的大小问题。我还要指出,您的 Delphi 代码已经成熟,可以缓冲区溢出。 @jdwengEncoding.Default
在这里适合匹配 Delphi 代码。我没有看到输入字符串是 UTF-8 编码的证据。 ANSI 是 Delphi 2007 的标准。
【参考方案1】:
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
struct command_data
public ushort direction;
[MarshalAs( UnmanagedType.ByValTStr, SizeConst = 10)]
public string name;
【讨论】:
这里有一些解释会很好以上是关于C# Struct 到字节数组的编组方式与 Delphi 中的打包记录相同的主要内容,如果未能解决你的问题,请参考以下文章