Oppure

Loading
18/11/09 19:38
xeeynamo
Ciao!
Ho un problema nel convertire un array di byte in un semplice oggetto... Ad esempio ho una classe di questo tipo
public class Ciao{
    public byte v1;
    public int v2, v3;
}

e il codice che tento di fare è un qualcosa di molto simile:
Ciao ciao;
FileStream file = new FileStream("file.bin", FileMode.Open);
byte[] data = new data[9];
file.Read(data, 0, data.Length);
ciao = (Ciao)data; // La parte che ovviamente non mi funziona

Ad esempio nella classe Ciao ho un byte e due int, con un totale di 9 bytes, quindi ho bisogno di leggere i 9 byte dal file in lettura e assegnarlo completamente alla classe ciao. Lo sò che potrei fare tipo ciao.v1 = file.ReadByte() però dato che devo gestire moltissimi dati (sarà 3.9kb in totale) verrebbe un codice troppo lungo e poco performante. Consigli? Aiuti? Dritte?
aaa
18/11/09 20:25
TheKaneB
noooo!!! avevo scritto un post molto dettagliato ma ho perso tutto perchè mi è scaduta la sessione di login... X_X

sono troppo pigro per riscriverlo per intero con la stessa cura XD

succo del discorso: il compilatore per ottimizzare la rapidità degli accessi alla memoria, riordina i membri di una classe o di una struct, sposta gli offset dei membri, in modo tale da renderli multipli dell'unità di memorizzazione di base (che dipende dall'architettura, generalmente 4 bytes su architetture a 32bit), e inserisce dei byte di padding (inutilizzati) nei buchi rimasti dentro la struct/classe.

Quindi i dati che leggi da file oltre ad avere una dimensione inferiore a quella della classe (a causa dei bytes di padding), hanno anche un ordine diverso rispetto a quello che hai specificato tu nel sorgente.
Il modo più semplice ed efficiente per ovviare al problema è quello di anteporre alla dichiarazione della struct/classe l'attributo __packed (questo su GCC; non ricordo l'equivalente per i compilatori microsoft). In questo modo il compilatore sa che deve mantenere la struct/classe nella forma esatta del sorgente.

Ciao ;)
aaa
18/11/09 21:15
xeeynamo
Uffi peccato, una risposta più approfondita mi sarebbe piaciuta di più XD. Cmq il concetto l'ho capito :). Ho fatto una breve ricerca su internet e purtroppo non ho trovato niente su una cosa equivalente al package sul C#... A questo punto mi dovrei creare una libreria in C che svolge questa funzione e poi richiamarla dal mio programma in C# giusto? Ma non ci sono sistemi più semplici? Meno codice ingombro nel mio progetto meglio è :D
aaa
19/11/09 10:34
Il Totem
In .NET l'attributo si chiama StructLayout ed è applicabile solo alle strutture. Ho scritto un articolo tempo fa:
pierotofy.it/pages/guide_tutorials/Visual_Basic/Risparmiare_memoria_usando_le_strutture/

Ad ogni modo, se in C questo è possibile, in .NET no. Anche ponendo quell'attributo non fai altro che riordinare i membri, ma rimane il fatto che non puoi convertire un array di byte in una struttura (o classe).
Per poterlo fare, devi definire una nuova versione dell'operatore CType per la tua struttura:
totem.altervista.org/guida/versione3/…

Ma anche questo è meno efficiente della serializzazione. Questa tecnica ti permette di convertire qualsiasi tipo di dato in uno stream di dati binari, e di riconvertirli in oggetto successivamente usando non più di cinque o sei righe di codice:
totem.altervista.org/guida/versione2/…

Questi due metodi generics che ho scritto in un vecchio progetto possono salvare e caricare qualsiasi tipo di oggetto:
''' <summary>
    ''' Serializza qualsiasi oggetto.
    ''' </summary>
    ''' <typeparam name="T">Un qualsiasi tipo serializzabile.</typeparam>
    ''' <param name="File">Nome del file su quale serializzare l'oggetto.</param>
    ''' <param name="Graph">Oggetto da serializzare.</param>
    ''' <remarks>Se il file esiste, viene sovrascritto.</remarks>
    Public Sub Serialize(Of T)(ByVal File As String, ByVal Graph As T)
        Dim Serializer As New Runtime.Serialization.Formatters.Binary.BinaryFormatter()
        Dim Stream As New IO.FileStream(File, IO.FileMode.Create)
        Serializer.Serialize(Stream, Graph)
        Stream.Close()
        Serializer = Nothing
    End Sub

    ''' <summary>
    ''' Deserializza qualsiasi oggetto.
    ''' </summary>
    ''' <typeparam name="T">Un qualsiasi tipo serializzabile.</typeparam>
    ''' <param name="File">Nome del file da cui deserializzare l'oggetto.</param>
    ''' <returns>Se il file non esiste, restituisce Nothing.</returns>
    Public Function Deserialize(Of T)(ByVal File As String) As T
        Dim Serializer As New Runtime.Serialization.Formatters.Binary.BinaryFormatter()
        Try
            Dim Stream As New IO.FileStream(File, IO.FileMode.Open)
            Dim Result As T
            Result = Serializer.Deserialize(Stream)
            Stream.Close()
            Serializer = Nothing
            Return Result
        Catch Ex As Exception

        Finally
            Serializer = Nothing
        End Try
        Return Nothing
    End Function

Ovviamente, se devi salvare più oggetti serializzerai una lista o un array di oggetti.
aaa
19/11/09 16:00
xeeynamo
Credo di aver trovato il modo per farlo!!!
public struct Ciao
{
    public byte v1;
    public int v2, v3;
    public byte v4;
    public ushort v5;
    public Ciao(bool reset)
    {
        v1 = 1;
        v2 = 0;
        v3 = 0;
        v4 = 0;
        v5 = 0;
    }
}

questa è la struttura
byte[] data = new byte[12] { 10, 50, 60, 70, 80, 10, 20, 30, 40, 99, 12, 34 };
Ciao ciao = new Ciao(true);

IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(ciao));
Marshal.StructureToPtr(ciao, ptr, true);
Marshal.Copy(data, 0, ptr, data.Length);


Questo è il codice... Funziona, ma il problema è che la srruttura non me la tocca proprio =( cioè che dopo questa operazione, v1 rimane uguale ad 1 e gli altri rimangono uguali a 0...
aaa
20/11/09 12:19
Il Totem
Certo, il marshalling è permesso con le strutture. Non l'ho scritto perchè pensavo ti bastassero tutti gli altri metodi.

Ma i valori li hai controllati in ciao? Perchè se è così, è normale: la funzione StructureToPtr copia solamente il contenuto di ciao nella memoria non gestita iniziante in ptr. Se poi gli copi dentro altri valori, è sempre quella memoria non gestita ad essere modificata. Quindi per ottenere la struttura modificata devi tornare indietro con PtrToStructure:
Ciao j = (Ciao)Marshal.PtrToStructure(ptr, typeof(Ciao));

Ultima modifica effettuata da Il Totem 20/11/09 12:20
aaa