У меня есть система, куда удаленный агент отправляет сериализированные структуры (от встроенной системы 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' в этом примере должен быть прямым порядком байтов для отображения пользователю. Я не хочу должным быть подкачивать порядок байтов в особенном методе структуры, так как это - универсальная проблема.
Моя первая мысль состояла в том, чтобы использовать Отражение, но я не очень знаком с той функцией. Кроме того, я надеялся, что будет лучшее решение там, что кто-то мог указать на меня к. Заранее спасибо :)
Отражение действительно кажется единственным реальным способом достичь того, что вам нужно.
Я собрал код ниже. Он создает атрибут с именем 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;
}