2010-10-05 3 views
0

J'essaie de remplir une structure (ne doit pas nécessairement être une structure réelle), avec des données chargées à partir d'un octet [].Chargement de données binaires dans une structure

Il existe de nombreuses structures de données dans l'octet [], l'un d'eux est une chaîne, qui est déclarée comme:

UInt16 stringLenght 
byte[stringLenght] zeroTerminatedString 

Je langue « c » cela pourrait être manipulé en déclarant une struct taille fixe , et à la place de la structure contenant la chaîne actuelle, faites un pointeur vers la chaîne.

Quelque chose comme:

UInt16 stringLength 
char* zeroTerminatedString 

Y at-il un (intelligent) façon de faire quelque chose de similaire en C#? Je veux dire charger des données binaires à partir d'un fichier/mémoire et le remplir dans une structure?

Cordialement Jakob Justesen

+2

Votre code C proposé est défectueux, donc vos chances de le convertir en C# sont vouées à l'échec! –

+0

Cela dépend. Combien d'enregistrements de données? À quel point sont-ils gros? –

Répondre

0

Le Marshal devrait être en mesure de le faire pour vous. Notez que cela ne peut être fait que pour une structure et vous aurez probablement besoin d'utiliser l'attribut StructLayout. Je ne suis pas sûr à 100% sur la façon de gérer la chaîne ou les tableaux, mais BStrWrapper ou ArrayWithOffset peut aider, également à l'affût de classes/attributs similaires (je sais que j'ai fait des choses comme ça avant de se lier à fonctions natives).

4

Ce n'est pas comment vous déclariez en C. Si l'enregistrement dans le fichier contient une chaîne alors vous déclarez la structure similaire à:

struct Example { 
    int mumble; // Anything, not necessarily a string length 
    char text[42]; 
    // etc... 
}; 

La déclaration équivalente C# ressemblerait à ceci:

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] 
    private struct Example { 
     public int mumble; 
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 42)] 
     public string text; 
     // etc... 
    } 

Vous utiliseriez normalement BinaryReader pour lire les données. Mais il ne peut pas gérer les chaînes de cette manière directement, vous devez les lire comme un byte [] et faire la conversion de chaîne vous-même. Vous ne pouvez pas non plus profiter de la syntaxe déclarative, vous devez écrire un appel pour chaque membre de la structure.

Il existe une solution de contournement pour cela, la classe Marshal sait déjà comment convertir les structures non gérées en structures managées avec la méthode PtrToStructure(). Voici une implémentation générique, cela fonctionne pour n'importe quel type blittable. Deux versions, une version statique qui lit à partir d'un octet [] et une méthode d'instance qui a été optimisée pour lire plusieurs fois à partir d'un flux. Vous utiliseriez un FileStream ou MemoryStream avec celui-là.

using System; 
using System.IO; 
using System.Runtime.InteropServices; 

class StructTranslator { 
    public static bool Read<T>(byte[] buffer, int index, ref T retval) { 
     if (index == buffer.Length) return false; 
     int size = Marshal.SizeOf(typeof(T)); 
     if (index + size > buffer.Length) throw new IndexOutOfRangeException(); 
     var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); 
     try { 
      IntPtr addr = (IntPtr)((long)handle.AddrOfPinnedObject() + index); 
      retval = (T)Marshal.PtrToStructure(addr, typeof(T)); 
     } 
     finally { 
      handle.Free(); 
     } 
     return true; 
    } 

    public bool Read<T>(Stream stream, ref T retval) { 
     int size = Marshal.SizeOf(typeof(T)); 
     if (buffer == null || size > buffer.Length) buffer = new byte[size]; 
     int len = stream.Read(buffer, 0, size); 
     if (len == 0) return false; 
     if (len != size) throw new EndOfStreamException(); 
     var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); 
     try { 
      retval = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); 
     } 
     finally { 
      handle.Free(); 
     } 
     return true; 
    } 

    private byte[] buffer; 
} 

Non testé, espérons que cela fonctionne.

Questions connexes