Marshal.PtrToStructure(然后再次返回)和字节顺序交换的通用解决方案
我有一个系统,其中远程代理发送序列化结构(来自嵌入式 C 系统)供我通过 IP/UDP 读取和存储。在某些情况下,我需要发回相同的结构类型。我认为我使用 Marshal.PtrToStructure (接收)和 Marshal.StructureToPtr (发送)进行了很好的设置。然而,一个小问题是网络大端整数需要转换为我的 x86 小端格式才能在本地使用。当我再次发送它们时,大端是要走的路。
以下是有问题的函数:
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,但我对该功能不是很熟悉。另外,我希望有人能指出更好的解决方案。提前致谢 :)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
反思似乎是实现你所追求的目标的唯一真正方法。
我在下面整理了一些代码。它创建一个名为
EndianAttribute
的属性,可以在结构体的字段级别应用该属性。我已经包含了此属性的定义及其关联的枚举,以及使用它所需的对代码的修改。附带说明一下,您不需要将
rawData
定义为ref
参数。请注意,这确实需要使用 C# 3.0/.NET 3.5,因为我在执行该工作的函数中使用 LINQ 和匿名类型。不过,如果没有这些功能,重写该函数并不困难。
Reflection does seem like the only real way to accomplish what you're after.
I've put together some code below. It creates an attribute called
EndianAttribute
that can be applied at the field level on a struct. I've included the definition for this attribute and it's associated enum, as well as the modifications to your code necessary to use it.As a side note, you did not need to define
rawData
as aref
parameter.Note that this does require the use of C# 3.0/.NET 3.5, since I'm using LINQ and anonymous types in the function doing the work. It would not be difficult to rewrite the function without these features, though.
对于我们这些没有 Linq 的人来说,可以使用
RespectEndianness()
替代:For those of us without Linq, a replacement
RespectEndianness()
:这是我的变体 - 它处理嵌套结构和数组,假设数组具有固定大小,例如用 [MarshalAs(UnmanagedType.ByValArray, SizeConst = N)] 属性标记。
Here's my variation - it handles nested structs and arrays, with the assumption that arrays are of a fixed size, eg marked with a [MarshalAs(UnmanagedType.ByValArray, SizeConst = N)] attribute.
这个问题太棒了,对我帮助很大!我需要扩展字节序更改器,因为它似乎无法处理结构中的数组或结构。
请注意,此代码仅在由 int、float、double 组成的结构上进行了测试。如果里面有一根绳子,可能会搞砸!
This question was awesome and helped me a lot! I needed to expand on the endian changer though as it doesn't seem to handle arrays or structs within structs.
Note this code was only tested on structs made of int, float, double. Will probably mess up if you have a string in there!