2015-02-17 5 views
11

Ich erhalte ein Array von Bytes von einem Socket und die Struktur der Bytes ist einfach ein großes char-Array von Strings mit fester Breite. In einigen Fällen ist das letzte Feld dynamisch (statt fester Länge) und ich versuche, die Bytes zu einer Struktur zu Marshalen. Ich habe gelesen, dass das Char-Array variabler Länge IntPtr sein muss, aber ich habe nicht herausgefunden, wie man es mit den verbleibenden Bytes marshale. Ich habe auch in einigen Artikeln gelesen, dass ich vielleicht eine zweite Struktur brauche, aber immer noch nicht herausfinden kann, wie man es richtig marshallen kann.byte [] Array zu Struktur mit Array variabler Länge

Hier ist eine solche site

Was ist der richtige Weg ist mit variabler Länge char-Arrays in Strukturen zu tun?

Die Struktur:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
public struct Header 
{ 
    #region private member fields 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] 
    private char[] _f1; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] 
    private char[] _f2; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] 
    private char[] _f3; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] 
    private char[] _f4; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] 
    private char[] _f5; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] 
    private char[] _f6; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] 
    private char[] _f7; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] 
    private char[] _f8; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] 
    private char[] _f9; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] 
    private char[] _f10; 

    // how would this get filled with a char[] array from the byte array? 
    public IntPtr VariableLengthData; 

    #endregion 
} 

Die Funktion:

public static Header FromArray(byte[] array) 
{ 
    IntPtr buff = IntPtr.Zero; 

    try 
    { 
     int objsize = Marshal.SizeOf(typeof(Header)); 
     buff = Marshal.AllocHGlobal(objsize); 
     Marshal.Copy(array, 0, buff, objsize); 
     var result = (Header)Marshal.PtrToStructure(buff, typeof(HostHeader)); 

     // the variable length data needs to be filled in somehow 
     // but also note that an extra 4 bytes was added to the size 
     // of the struct with the IntPtr 
     if(objsize < array.Length) 
     { 
      Marshal.Copy(array, array.Length - objsize, result.VariableLengthData, array.Length - objsize); 
     } 

     return result; 
    } 
    finally 
    { 
     if (buff != IntPtr.Zero) 
     { 
      Marshal.FreeHGlobal(buff); 
      buff = IntPtr.Zero; 
     } 
    } 
} 

Dies funktioniert - aber jetzt Marshal.SizeOf (headerObj) sagt, es ist kleiner, als es wirklich ist, wenn ich versuche, es zu konvertieren zurück zu ein byte [] Array. Abgesehen davon ist irgendetwas falsch mit dieser Lösung?

+0

Sie sollten eine tatsächliche Frage neu formulieren und eingeben. –

+0

Array + 160 ist der Rest Ihrer Daten. 'Encoding.ASCII.GetString (Array, 160, Array.Length-160)' – EZI

+0

@ EZI - das ist richtig. Wenn das Byte-Array größer als 160 ist, ist alles andere ein Feld variabler Länge. –

Antwort

10

Versuchen Sie nicht zu sehr, dass Ihre C# -Deklarationen genau dem Paketformat entsprechen. Sie würden dies bevorzugen:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] 
public class Header { 
    //.... 
} 

public class Packet { 
    public Header Header; 
    public byte[] VariableLengthData; 
} 

Jetzt wird es einfach. Sie können den Header in einem Schlage Marschall und kopieren Sie einfach, was zusätzliche Bytes vorhanden sind:

static unsafe Packet FromArray(byte[] data) { 
    var hdrsize = Marshal.SizeOf(typeof(Header)); 
    if (data.Length < hdrsize) throw new ArgumentException(); 
    Packet result = new Packet(); 
    // Marshal the header 
    fixed (byte* pdata = &data[0]) { 
     result.Header = (Header)Marshal.PtrToStructure(new IntPtr(pdata), typeof(Header)); 
    } 
    // Copy the rest 
    var varsize = data.Length - hdrsize; 
    result.VariableLengthData = new byte[varsize]; 
    Array.Copy(data, hdrsize, result.VariableLengthData, 0, varsize); 
    return result; 
} 

und die Paketklasse ändern, wie Sie sehen, passen, werden Sie wahrscheinlich eine Reihe von Eigenschaften, um mit dem char [hinzufügen möchten ].

+0

Ich dachte auch über diese Lösung nach, aber lese auch einen Artikel, der mit "public fixed char Data [1]" auch dieses Problem behebt, aber ich habe nicht die vollständige Quelle, um zu bestimmen, wie es verwendet oder gemarshallt wurde. Ich gehe vielleicht aus Sicherheitsgründen auf diesen Weg, aber wissen Sie, was sie damit im folgenden Artikel meinen? http://www.experts-exchange.com/Programming/Languages/C_Sharp/Q_27480829.html –

+0

Ich nahm Ihre Idee und machte es Teil der ursprünglichen Struktur, die möglicherweise nicht die beste Lösung (siehe Frage zum Beispiel), und Es scheint für die Deserialisierung zu funktionieren, aber serialisiert es zurück zu einem Array byte [], ich würde die tatsächliche Größe des Objekts und nicht nur die Größe von (Header) benötigen. Gedanken?? –

+0

Ich dachte über ein Ein-Byte-Array nach, aber ich ignorierte es. Ihr Programm kann mit einem AVE abstürzen, wenn Sie jemals ein Paket ohne zusätzliche Daten erhalten. Vielleicht können Sie eine Garantie bekommen, dass es immer welche gibt, ich könnte nicht die gleiche Annahme machen. –