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

10-10 21:36