2015-02-17 30 views
11

Ricevo una matrice di byte da un socket e la struttura dei byte è semplicemente un grande array di caratteri di stringhe a larghezza fissa. In alcuni casi, l'ultimo campo è dinamico (invece di lunghezza fissa) e sto cercando di eseguire il marshalling dei byte su una struttura. Ho letto che il char array a lunghezza variabile deve essere IntPtr, ma non ho capito come gestirlo con i restanti byte. Ho anche letto in alcuni articoli che potrei aver bisogno di una seconda struttura, ma non riesco ancora a capire come procedere correttamente con Marshal.byte [] array a struct con array a lunghezza variabile

Ecco uno di questi site

Qual è il modo corretto di affrontare con gli array char di lunghezza variabile a struct?

La struct:

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

La funzione:

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

Questo funziona - ma ora Marshal.SizeOf (headerObj) dice che è più piccolo di quanto non sia in realtà quando tento di riconvertirlo un array di byte []. A parte questo, c'è qualcosa di sbagliato in questa soluzione?

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
public struct Header 
{ 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 
    public char[] Field1; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 
    public char[] Field2; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 
    public char[] Field3; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] 
    public char[] Field4; 
} 

public static Header DeserializeHeader(byte[] data) 
{ 
    int objsize = Marshal.SizeOf(typeof(Header)); 
    IntPtr buff = Marshal.AllocHGlobal(objsize); 
    Marshal.Copy(data, 0, buff, objsize); 
    var header = (Header)Marshal.PtrToStructure(buff, typeof(Header)); 
    Marshal.FreeHGlobal(buff); 

    // resize Field4 to hold all the remaining bytes 
    if(objsize < data.Length) 
    { 
     header.Field4 = Encoding.ASCII.GetChars(data, objsize - header.Field4.Length, data.Length - objsize - header.Field4.Length); 
    } 
    return header; 
} 
+0

È necessario riformulare e inserire una domanda reale. –

+0

array + 160 è il resto dei dati. 'Encoding.ASCII.GetString (array, 160, array.Length-160)' – EZI

+0

@EZI - che è corretto. Se l'array di byte è maggiore di 160, tutto il resto è un campo di lunghezza variabile. –

risposta

10

Basta non sforzarsi troppo per rendere le dichiarazioni C# corrispondenti esattamente al formato del pacchetto. Preferisci questo:

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

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

Ora diventa semplice. È possibile maresciallo l'intestazione in un colpo solo e basta copiare qualunque byte in più sono presenti:

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

E modificare la classe Packet come meglio credi, probabilmente si desidera aggiungere un po 'di oggetti da trattare con il char [ ].

+0

Ho preso in considerazione anche questa soluzione, ma ho anche letto un articolo che utilizza "public fixed char Data [1]" risolve questo problema, ma non ho la fonte completa per determinare come è stato utilizzato o il marshalling. Potrei finire su questa rotta per motivi di sicurezza, ma sai cosa significano in questo senso nel seguente articolo? http://www.experts-exchange.com/Programming/Languages/C_Sharp/Q_27480829.html –

+0

Ho preso la tua idea e l'ho resa parte della struttura originale, che potrebbe non essere la soluzione migliore (vedi la domanda per esempio), e sembra funzionare per la deserializzazione, ma serializzandola su una matrice di byte [], mi servirebbe la dimensione effettiva dell'oggetto e non solo la dimensione di (Header). Pensieri?? –

+0

Ho considerato una matrice di un byte ma l'ho chiusa. Il tuo programma può bloccarsi con un AVE se ricevi un pacchetto senza dati aggiuntivi. Forse puoi avere la garanzia che ce ne sono sempre, non potrei fare la stessa ipotesi. –