关于C# byte[]与struct的转换
转自:http://blog.chinaunix.net/uid-215617-id-2213082.html
Some of the C# code I've been writing recently communicates via TCP/IP with legacy C++ applications. These applications use a raw packet format where C/C++ structures are passed back and forth.
Here is a simplified example of what the legacy code could look like:
#pragma pack(1) typedef struct { int id; char[50] text; } MESSAGE; // Send a message MESSAGE msg; msg.id = 1; strcpy(msg.text, "This is a test"); send(socket, (char*)&msg); // Receive a message char buffer[100]; recv(socket, buffer, 100); MESSAGE* msg = (MESSAGE*)buffer; printf("id=%d\n", msg->id); printf("text=%s\n", msg->text);
The problem I was faced with was how to receive and handle this kind of message in a C# application. One method is to use BitConverter and Encoding.ASCII to grab the data field by field. This is tedious, prone to errors and easy to break of modifications are made in the future.
A better method is to marshal the byte array to a C# structure. Here is an example of how to do that marshaling:
using System; using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential, Pack=1)] struct Message { public int id; [MarshalAs (UnmanagedType.ByValTStr, SizeConst=50)] public string text; } void OnPacket(byte[] packet) { GCHandle pinnedPacket = GCHandle.Alloc(packet, GCHandleType.Pinned); Message msg = (Message)Marshal.PtrToStructure( pinnedPacket.AddrOfPinnedObject(), typeof(Message)); pinnedPacket.Free(); }
The GCHandle.Alloc call pins the byte[] in memory so the garbage collector doesn't mess with it. The AddrOfPinnedObject call returns an IntPtr pointing to the start of the array and the Marshal.PtrToStructure does the work of marshaling the byte[] to the structure.
If the actual structure data didn't start at the beginning of the byte array you would use the following assuming the structure data starts at position 10 of the array:
Message p = (Message)Marshal.PtrToStructure( Marshal.UnsafeAddrOfPinnedArrayElement(pinnedPacket, 10), typeof(Message));
[Serializable()]
public struct frame_t : ISerializable
{
//char数组,SizeConst表示数组的个数,在转换成
//byte数组前必须先初始化数组,再使用,初始化
//的数组长度必须和SizeConst一致
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public char[] headers;
public int nbframe;
public double seqtimes;
public int deltatimes;
public int w;
public int h;
public int size;
public int format;
public ushort bright;
public ushort contrast;
public ushort colors;
public ushort exposure;
public byte wakeup;
public int acknowledge;
{
throw new Exception("The method or operation is not implemented.");
}
};
{
//struct转换为byte[]
public static byte[] StructToBytes(object structObj)
{
int size = Marshal.SizeOf(structObj);
IntPtr buffer = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(structObj, buffer, false);
byte[] bytes = new byte[size];
Marshal.Copy(buffer, bytes, 0, size);
return bytes;
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
//byte[]转换为struct
public static object BytesToStruct(byte[] bytes, Type strcutType)
{
int size = Marshal.SizeOf(strcutType);
IntPtr buffer = Marshal.AllocHGlobal(size);
try
{
Marshal.Copy(bytes, 0, buffer, size);
return Marshal.PtrToStructure(buffer, strcutType);
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
2015-10-26 签名时加密失败 --“对程序集签名时出错 - 拒绝访问
2015-10-26 EditPlus(4.0.0.395)中文免激活绿色版