Vă mulțumim pentru susținere

Citirea unei structuri de date C / C ++ în C # dintr-o matrice octet

Care ar fi cel mai bun mod de a umple un C # struct dintr-un tablou [] unde datele au fost de la un struct C / C ++? Structura C ar arata cam asa (C-ul meu este foarte ruginit):

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

Și ar umple astfel:

[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;
}

Ce este cel mai bun mod de a copia OldStuff la NewStuff , dacă OldStuff a fost trecut ca array byte []?

În prezent, fac ceva de genul următor, dar se simte cam nebun.

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();

Există o modalitate mai bună de a realiza acest lucru?


Ar folosind BinaryReader clasa oferă nici o performanță peste câștiguri pinning memorie și utilizând Marshal.PtrStructure ?

0
adăugat editat
Cum puteți face acest lucru la nivelul structurii, adică fără a trebui să inversați individual octeții pentru fiecare valoare din struct?
adăugat autor Pat
FYI, dacă programul dvs. rulează pe diverse mașini, este posibil să aveți nevoie să vă ocupați de micul endian.
adăugat autor KPexEA

5 răspunsuri

Din ceea ce văd în acest context, nu trebuie să copiați SomeByteArray într-un buffer. Pur și simplu trebuie să obțineți mânerul de la SomeByteArray , să îl copiați, să copiați IntPtr folosind PtrToStructure și apoi să eliberați. Nu este nevoie de o copie.

Asta ar fi:

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;
}

Versiune generică:

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

Versiune mai simplă (necesită comutator nesigur ):

unsafe T ByteArrayToStructure(byte[] bytes) where T : struct
{
    fixed (byte* ptr = &bytes[0])
    {
        return (T)Marshal.PtrToStructure((IntPtr)ptr, typeof(T));
    }
}
0
adăugat
CS0411 Argumentele de tip pentru metoda ByteArrayToStructure (byte [], int) nu pot fi deduse din utilizare. Încercați să specificați în mod explicit argumentele de tip. (Am adăugat index int de byte matrice) la ea.
adăugat autor SSpoke
Va scurgeri de memorie în prezența unor excepții. Consultați: stackoverflow.com/a/41836532/184528 pentru o versiune mai sigură.
adăugat autor cdiggins
Începând cu 4.5.1, există o versiune generică a PtrToStructure, astfel încât a doua linie din versiunea generică de mai sus poate deveni: var stuff = Marshal.PtrToStructure (handle.AddrOfPinnedObject ());
adăugat autor RobinHood70

Aici este o versiune excepțională în siguranță a răspunsului acceptat :

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

Dacă aveți un byte [], ar trebui să puteți utiliza clasa BinaryReader și să setați valori pe NewStuff utilizând metodele ReadX disponibile.

0
adăugat

Ferește-te pentru probleme de ambalare. În exemplul pe care l-ați dat, toate câmpurile se află la compensările evidente, deoarece totul se află la 4 octeți, dar acest lucru nu va fi întotdeauna cazul. Fișierele Visual C ++ pe limite de 8 octeți implicit.

0
adăugat
"Visual C ++ pachete pe 8 byte limite în mod implicit." Acest lucru mi-a rezolvat problema, multumesc mult!
adăugat autor Chris L
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;
}   

Ia asta

0
adăugat