88 votes

Comment faire pour convertir une structure d'un tableau d'octets en C#?

Comment puis-je convertir une structure d'un tableau d'octets en C#?

J'ai défini une structure comme ceci:

public struct CIFSPacket
{
    public uint protocolIdentifier; //The value must be "0xFF+'SMB'".
    public byte command;

    public byte errorClass;
    public byte reserved;
    public ushort error;

    public byte flags;

    //Here there are 14 bytes of data which is used differently among different dialects.
    //I do want the flags2. However, so I'll try parsing them.
    public ushort flags2;

    public ushort treeId;
    public ushort processId;
    public ushort userId;
    public ushort multiplexId;

    //Trans request
    public byte wordCount;//Count of parameter words defining the data portion of the packet.
    //From here it might be undefined...

    public int parametersStartIndex;

    public ushort byteCount; //Buffer length
    public int bufferStartIndex;

    public string Buffer;
}

Dans ma méthode principale, j'ai créer une instance d'elle et de lui affecter des valeurs:

CIFSPacket packet = new CIFSPacket();
packet.protocolIdentifier = 0xff;
packet.command = (byte)CommandTypes.SMB_COM_NEGOTIATE;
packet.errorClass = 0xff;
packet.error = 0;
packet.flags = 0x00;
packet.flags2 = 0x0001;
packet.multiplexId = 22;
packet.wordCount = 0;
packet.byteCount = 119;

packet.Buffer = "NT LM 0.12";

Maintenant, je veux envoyer ce Paquet par socket. Pour cela, j'ai besoin de convertir la structure d'un tableau d'octets. Comment puis-je le faire?

Mon code est comme suit.

static void Main(string[] args)
{

  Socket MyPing = new Socket(AddressFamily.InterNetwork,
  SocketType.Stream , ProtocolType.Unspecified ) ;


  MyPing.Connect("172.24.18.240", 139);

    //Fake an IP Address so I can send with SendTo
    IPAddress IP = new IPAddress(new byte[] { 172,24,18,240 });
    IPEndPoint IPEP = new IPEndPoint(IP, 139);

    //Local IP for Receiving
    IPEndPoint Local = new IPEndPoint(IPAddress.Any, 0);
    EndPoint EP = (EndPoint)Local;

    CIFSPacket packet = new CIFSPacket();
    packet.protocolIdentifier = 0xff;
    packet.command = (byte)CommandTypes.SMB_COM_NEGOTIATE;
    packet.errorClass = 0xff;
    packet.error = 0;
    packet.flags = 0x00;
    packet.flags2 = 0x0001;
    packet.multiplexId = 22;
    packet.wordCount = 0;
    packet.byteCount = 119;

    packet.Buffer = "NT LM 0.12";

    MyPing.SendTo(It takes byte array as parameter);
}

Que serait un extrait du code de l'être?

134voto

Vincent McNabb Points 5747

C'est assez facile, à l'aide de la sérialisation.

En haut du fichier

using System.Runtime.InteropServices

La fonction

byte[] getBytes(CIFSPacket str) {
    int size = Marshal.SizeOf(str);
    byte[] arr = new byte[size];
    IntPtr ptr = Marshal.AllocHGlobal(size);

    Marshal.StructureToPtr(str, ptr, true);
    Marshal.Copy(ptr, arr, 0, size);
    Marshal.FreeHGlobal(ptr);

    return arr;
}

Et pour la convertir:

CIFSPacket fromBytes(byte[] arr) {
    CIFSPacket str = new CIFSPacket();

    int size = Marshal.SizeOf(str);
    IntPtr ptr = Marshal.AllocHGlobal(size);

    Marshal.Copy(arr, 0, ptr, size);

    str = (CIFSPacket)Marshal.PtrToStructure(ptr, str.GetType());
    Marshal.FreeHGlobal(ptr);

    return str;
}

Dans votre structure, vous aurez besoin de mettre ce avant une chaîne de caractères

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string Buffer;

Et assurez-vous que SizeConst est aussi gros que votre plus grande chaîne possible.

Et vous devriez lire ceci: http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

34voto

Paul Points 21

Si vous voulez vraiment être RAPIDE, vous pouvez le faire en utilisant le code unsafe avec CopyMemory. CopyMemory est d'environ 5x plus rapide (par exemple, 800 MO de données prend 3s de copie via le regroupement, tout en prenant seulement .6s pour copier via CopyMemory). Cette méthode ne vous limite à l'utilisation de données qui est stockée dans la structure de la goutte elle-même, par exemple, des numéros, de longueur fixe ou de tableaux d'octets.

    [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
    private static unsafe extern void CopyMemory(void *dest, void *src, int count);

    private static unsafe byte[] Serialize(TestStruct[] index)
    {
        var buffer = new byte[Marshal.SizeOf(typeof(TestStruct)) * index.Length];
        fixed (void* d = &buffer[0])
        {
            fixed (void* s = &index[0])
            {
                CopyMemory(d, s, buffer.Length);
            }
        }

        return buffer;
    }

26voto

Abdel Olakara Points 11016

Jetez un oeil à ces méthodes:

byte [] StructureToByteArray(object obj)
{
    int len = Marshal.SizeOf(obj);

    byte [] arr = new byte[len];

    IntPtr ptr = Marshal.AllocHGlobal(len);

    Marshal.StructureToPtr(obj, ptr, true);

    Marshal.Copy(ptr, arr, 0, len);

    Marshal.FreeHGlobal(ptr);

    return arr;
}

void ByteArrayToStructure(byte [] bytearray, ref object obj)
{
    int len = Marshal.SizeOf(obj);

    IntPtr i = Marshal.AllocHGlobal(len);

    Marshal.Copy(bytearray,0, i,len);

    obj = Marshal.PtrToStructure(i, obj.GetType());

    Marshal.FreeHGlobal(i);
}

C'est une honteuse copie d'un autre thread que j'ai trouvé après Googler!

Mise à jour : Pour plus de détails, voir la source

2voto

x77 Points 443

Vous pouvez utiliser le Maréchal (StructureToPtr, ptrToStructure), et le Maréchal.copie, mais c'est plataform dépendante.


La sérialisation comprend des Fonctions pour la Sérialisation Personnalisée.

public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext) 

SerializationInfo inclure des fonctions de sérialiser chaque membre.


BinaryWriter et BinaryReader contient également des méthodes pour Enregistrer / Charger à Tableau d'Octets (Stream).

Notez que vous pouvez créer un MemoryStream à partir d'un Tableau d'Octets ou un Tableau d'Octets à partir d'un MemoryStream.

Vous pouvez créer une méthode d'Enregistrer et d'une méthode Nouvelle de votre structure:

   Save(Bw as BinaryWriter)
   New (Br as BinaryReader)

Ensuite, vous sélectionnez les membres à Enregistrer / Charger à Flux -> Tableau d'Octets.

1voto

DianeS Points 1

Cela peut être fait très simplement.

Définir votre struct explicitement [StructLayout(LayoutKind.Explicit)]

int size = list.GetLength(0);
IntPtr addr = Marshal.AllocHGlobal(size * sizeof(DataStruct));
DataStruct *ptrBuffer = (DataStruct *)addr;
foreach DataStruct ds in list)
    {
        *ptrBuffer = ds;
        ptrBuffer += 1;
    }

Ce code ne peut être écrite dans un contexte dangereux. Vous avez pour gratuit addr lorsque vous avez terminé avec elle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X