Marshal.PtrToStructure(和回来)和字节顺序交换通用的解决方案 [英] Marshal.PtrToStructure (and back again) and generic solution for endianness swapping

查看:452
本文介绍了Marshal.PtrToStructure(和回来)和字节顺序交换通用的解决方案的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个系统,其中的远程代理发送的序列化结构(从嵌入式C系),给我读,并通过IP / UDP店。在某些情况下,我需要发回相同的结构类型。我想我有一个不错的设置使用Marshal.PtrToStructure(接收)和Marshal.StructureToPtr(发送)。然而,一个小的小问题,就是需要在网络大端整数转换为我的86小端格式在本地使用。当我再次送他们,大端是要走的路。

I have a system where a remote agent sends serialized structures (from an embedded C system) for me to read and store via IP/UDP. In some cases I need to send back the same structure types. I thought I had a nice setup using Marshal.PtrToStructure (receive) and Marshal.StructureToPtr (send). However, a small gotcha is that the network big endian integers need to be converted to my x86 little endian format to be used locally. When I'm sending them off again, big endian is the way to go.

下面是功能问题:

    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;
    }

和可能被用来像这样一个简单的例子结构:

And a quick example structure that might be used like this:

byte[] data = this.sock.Receive(ref this.ipep);
Request request = BytesToStruct<Request>(ref data);

如果有问题的结构如下:

Where the structure in question looks like:

[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'应该是小端,用于显示给用户。我不希望有交换字节序的一个结构特殊的方式,因为这是一个普遍的问题。

What (generic) way can I swap the endianness when marshalling the structures? My need is such that the locally stored 'request.sequence' in this example should be little-endian for displaying to the user. I don't want to have to swap the endianness in a structure-specific way since it's a generic problem.

我的第一个念头是使用反射,但我不是很熟悉的功能。另外,我希望会有一个更好的解决方案,在那里,有人可以点我往。在此先感谢:)

My first thought was to use Reflection, but I'm not very familiar with that feature. Also, I hoped that there would be a better solution out there that somebody could point me towards. Thanks in advance :)

推荐答案

反思似乎喜欢来完成你追求的是什么的唯一途径。

Reflection does seem like the only real way to accomplish what you're after.

我已经把一些code以下。它创建了一个名为 EndianAttribute ,可以在一个结构中的字段级应用的属性。我已经包括此属性的定义和它相关的枚举,还有修改你需要code使用它。

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.

作为一个方面说明,你没有需要定义 RAWDATA REF 参数。

As a side note, you did not need to define rawData as a ref parameter.

请注意,这并不需要使用C#3.0 / .NET 3.5,因为我使用的功能LINQ和匿名类型做的工作。它不会是很难改写功能,没有这些功能,但。

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.

[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;
}

这篇关于Marshal.PtrToStructure(和回来)和字节顺序交换通用的解决方案的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆