Marshal.PtrToStructure(和回来)和字节顺序交换通用的解决方案字节、顺序、解决方案、Marshal

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

我有一个系统,其中的远程代理发送的序列化结构(从嵌入式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;
}
阅读全文

相关推荐

最新文章