gpt4 book ai didi

c# - Marshal.PtrToStructure(并再次返回)和字节顺序交换的通用解决方案

转载 作者:IT王子 更新时间:2023-10-29 04:49:13 30 4
gpt4 key购买 nike

我有一个系统,其中远程代理发送序列化结构(来自嵌入式 C 系统)供我通过 IP/UDP 读取和存储。在某些情况下,我需要发回相同的结构类型。我认为我使用 Marshal.PtrToStructure(接收)和 Marshal.StructureToPtr(发送)进行了很好的设置。但是,一个小问题是网络大端整数需要转换为我的 x86 小端格式才能在本地使用。当我再次将它们送走时,big 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,但我对该功能不是很熟悉。另外,我希望会有更好的解决方案,有人可以指出我的方向。提前致谢:)

最佳答案

反射(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/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com