Маршал. PtrToStructure (и назад снова) и универсальное решение для свопинга порядка байтов

У меня есть система, куда удаленный агент отправляет сериализированные структуры (от встроенной системы C), чтобы я считал и сохранил через IP/UDP. В некоторых случаях я должен передать те же типы структуры обратно. Я думал, что у меня была хорошая установка с помощью Маршала. PtrToStructure (получают) и Маршал. 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' в этом примере должен быть прямым порядком байтов для отображения пользователю. Я не хочу должным быть подкачивать порядок байтов в особенном методе структуры, так как это - универсальная проблема.

Моя первая мысль состояла в том, чтобы использовать Отражение, но я не очень знаком с той функцией. Кроме того, я надеялся, что будет лучшее решение там, что кто-то мог указать на меня к. Заранее спасибо :)

18
задан cgyDeveloper 12 April 2010 в 17:01
поделиться

1 ответ

Отражение действительно кажется единственным реальным способом достичь того, что вам нужно.

Я собрал код ниже. Он создает атрибут с именем 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;
}
20
ответ дан 30 November 2019 в 08:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: