바이트 배열에서 C #의 C / C ++ 데이터 구조 읽기


86

데이터가 C / C ++ 구조체에서 온 byte [] 배열에서 C # 구조체를 채우는 가장 좋은 방법은 무엇입니까? C 구조체는 다음과 같이 보일 것입니다 (내 C는 매우 녹슬 었습니다).

typedef OldStuff {
    CHAR Name[8];
    UInt32 User;
    CHAR Location[8];
    UInt32 TimeStamp;
    UInt32 Sequence;
    CHAR Tracking[16];
    CHAR Filler[12];
}

그리고 다음과 같이 채울 것입니다.

[StructLayout(LayoutKind.Explicit, Size = 56, Pack = 1)]
public struct NewStuff
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    [FieldOffset(0)]
    public string Name;

    [MarshalAs(UnmanagedType.U4)]
    [FieldOffset(8)]
    public uint User;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    [FieldOffset(12)]
    public string Location;

    [MarshalAs(UnmanagedType.U4)]
    [FieldOffset(20)]
    public uint TimeStamp;

    [MarshalAs(UnmanagedType.U4)]
    [FieldOffset(24)]
    public uint Sequence;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
    [FieldOffset(28)]
    public string Tracking;
}

byte [] 배열로 전달 된 경우 에 복사 OldStuff하는 가장 좋은 방법은 무엇입니까 ?NewStuffOldStuff

나는 현재 다음과 같은 것을하고 있지만 다소 투박한 것 같습니다.

GCHandle handle;
NewStuff MyStuff;

int BufferSize = Marshal.SizeOf(typeof(NewStuff));
byte[] buff = new byte[BufferSize];

Array.Copy(SomeByteArray, 0, buff, 0, BufferSize);

handle = GCHandle.Alloc(buff, GCHandleType.Pinned);

MyStuff = (NewStuff)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(NewStuff));

handle.Free();

이것을 달성하는 더 좋은 방법이 있습니까?


BinaryReader클래스를 사용하면 메모리를 고정하고 사용하는 것보다 성능이 향상 Marshal.PtrStructure됩니까?


1
참고로, 프로그램이 다양한 시스템에서 실행되는 경우 little vs big endian을 처리해야 할 수 있습니다.
KPexEA

1
즉, 구조체의 각 값에 대해 개별적으로 바이트를 반전 할 필요없이 구조체 수준에서 어떻게 처리 할 수 ​​있습니까?
Pat

답변:


114

그 맥락에서 볼 수 있듯이 SomeByteArray버퍼 에 복사 할 필요가 없습니다 . 에서 핸들을 가져 와서 SomeByteArray고정하고을 IntPtr사용 하여 데이터를 복사 PtrToStructure한 다음 해제하면됩니다. 사본이 필요하지 않습니다.

다음과 같습니다.

NewStuff ByteArrayToNewStuff(byte[] bytes)
{
    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try
    {
        NewStuff stuff = (NewStuff)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(NewStuff));
    }
    finally
    {
        handle.Free();
    }
    return stuff;
}

일반 버전 :

T ByteArrayToStructure<T>(byte[] bytes) where T: struct 
{
    T stuff;
    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try
    {
        stuff = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    }
    finally
    {
        handle.Free();
    }
    return stuff;
}

더 간단한 버전 ( unsafe스위치 필요 ) :

unsafe T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
    fixed (byte* ptr = &bytes[0])
    {
        return (T)Marshal.PtrToStructure((IntPtr)ptr, typeof(T));
    }
}

CS0411 'ByteArrayToStructure <T> (byte [], int)'메서드에 대한 형식 인수는 사용법에서 유추 할 수 없습니다. 형식 인수를 명시 적으로 지정해보십시오. (바이트 배열의 int 인덱스를 추가했습니다).
SSpoke

예외가있는 경우 메모리가 누수됩니다. 보다 안전한 버전은 stackoverflow.com/a/41836532/184528 을 참조하십시오 .
cdiggins

2
4.5.1부터 PtrToStructure의 제네릭 버전이 있으므로 위의 제네릭 버전의 두 번째 줄은 다음과 같습니다. var stuff = Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());

10

다음은 허용되는 답변 의 예외 안전 버전입니다 .

public static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
    var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try {
        return (T) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    }
    finally {
        handle.Free();
    }
}

3
@ Ben-Collins 내 답변을 추가 한 후 수락 된 답변이 편집되었습니다.
cdiggins

5

포장 문제에주의하십시오. 예제에서 모든 필드가 4 바이트 경계에 있기 때문에 모든 필드가 명백한 오프셋에 있지만 항상 그런 것은 아닙니다. Visual C ++ 팩은 기본적으로 8 바이트 경계에 있습니다.


1
"Visual C ++ 는 기본적 으로 8 바이트 경계로 압축됩니다 ." 이것은 내 문제를 해결했습니다. 감사합니다!
Chris L

4
object ByteArrayToStructure(byte[] bytearray, object structureObj, int position)
{
    int length = Marshal.SizeOf(structureObj);
    IntPtr ptr = Marshal.AllocHGlobal(length);
    Marshal.Copy(bytearray, 0, ptr, length);
    structureObj = Marshal.PtrToStructure(Marshal.UnsafeAddrOfPinnedArrayElement(bytearray, position), structureObj.GetType());
    Marshal.FreeHGlobal(ptr);
    return structureObj;
}   

이것을 가지고


0

byte []가있는 경우 BinaryReader 클래스를 사용하고 사용 가능한 ReadX 메서드를 사용하여 NewStuff에 값을 설정할 수 있어야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.