我有一个系统,其中远程代理发送序列化的结构(从嵌入式C系统)供我通过IP/UDP读取和存储。在某些情况下,我需要发送回相同的结构类型。我以为我使用Marshal.PtrToStructure(接收)和Marshal.StructureToPtr(发送)进行了很好的设置。但是,一个小问题是网络大字节序整数需要转换为我的x86 little endian格式才能在本地使用。当我再次发送它们时,大尾数法是可行的方法。
这里是有问题的功能:
private static T BytesToStruct<T>(ref byte[] rawData) where T: struct
{
T result = default(T);
GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
try
{
IntPtr rawDataPtr = handle.AddrOfPinnedObject();
result = (T)Marshal.PtrToStructure(rawDataPtr, typeof(T));
}
finally
{
handle.Free();
}
return result;
}
private static byte[] StructToBytes<T>(T data) 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();
}
return rawData;
}
还有一个可能像这样使用的快速示例结构:
byte[] data = this.sock.Receive(ref this.ipep);
Request request = BytesToStruct<Request>(ref data);
所讨论的结构如下所示:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
private struct Request
{
public byte type;
public short sequence;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public byte[] address;
}
编码结构时,我可以通过哪些(通用)方式交换字节序?我的需求是,在此示例中,本地存储的“request.sequence”应为低端字节序显示给用户。我不想以特定于结构的方式交换字节序,因为这是一个普遍的问题。
我最初的想法是使用反射,但是我对该功能不是很熟悉。另外,我希望有更好的解决方案,有人可以指出我的想法。提前致谢 :)
最佳答案
反射(reflection)似乎是完成所追求目标的唯一真实方法。
我在下面整理了一些代码。它创建一个称为EndianAttribute
的属性,该属性可以在结构的字段级别上应用。我已经包含了该属性的定义及其相关的枚举,以及使用该属性所必需的代码修改。
附带说明,您无需将rawData
定义为ref
参数。
请注意,这确实需要使用C#3.0/.NET 3.5,因为我在执行工作的函数中使用LINQ和匿名类型。但是,没有这些功能就可以很容易地重写该功能。
[AttributeUsage(AttributeTargets.Field)]
public class EndianAttribute : Attribute
{
public Endianness Endianness { get; private set; }
public EndianAttribute(Endianness endianness)
{
this.Endianness = endianness;
}
}
public enum Endianness
{
BigEndian,
LittleEndian
}
private static void RespectEndianness(Type type, byte[] data)
{
var fields = type.GetFields().Where(f => f.IsDefined(typeof(EndianAttribute), false))
.Select(f => new
{
Field = f,
Attribute = (EndianAttribute)f.GetCustomAttributes(typeof(EndianAttribute), false)[0],
Offset = Marshal.OffsetOf(type, f.Name).ToInt32()
}).ToList();
foreach (var field in fields)
{
if ((field.Attribute.Endianness == Endianness.BigEndian && BitConverter.IsLittleEndian) ||
(field.Attribute.Endianness == Endianness.LittleEndian && !BitConverter.IsLittleEndian))
{
Array.Reverse(data, field.Offset, Marshal.SizeOf(field.Field.FieldType));
}
}
}
private static T BytesToStruct<T>(byte[] rawData) where T : struct
{
T result = default(T);
RespectEndianness(typeof(T), rawData);
GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
try
{
IntPtr rawDataPtr = handle.AddrOfPinnedObject();
result = (T)Marshal.PtrToStructure(rawDataPtr, typeof(T));
}
finally
{
handle.Free();
}
return result;
}
private static byte[] StructToBytes<T>(T data) 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();
}
RespectEndianness(typeof(T), rawData);
return rawData;
}
关于c# - Marshal.PtrToStructure(并再次返回)和字节序交换的通用解决方案,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/2623761/