7
votes

Copier la matrice d'octet sur divers champs de classe / structure en C #

Dans l'exemple C # Code ci-dessous, j'ai une matrice d'octet qui a été lue à partir d'une prise. Je veux analyser les données dans les différents domaines de "Exampleclass" (8 premiers octets dans la variable 64 bits "Field1", suivant 4 octets dans la variable 32 bits "champ2", etc.)

using System;
namespace CsByteCopy
{
  class Program
  {
    class ExampleClass
    {
      public UInt64 field1;
      public UInt32 field2;
      public UInt16 field3;
      public byte[] field4 = new byte[18];
    }

    static void Main(string[] args)
    {
      byte[] exampleData =
      {
        // These 8 bytes should go in 'field1'
        0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
        // These 4 bytes should go in 'field2'
        0x08,0x09,0x0A,0x0B,
        // These 2 bytes should go in 'field3'
        0x0C,0x0D,
        // These 18 * 1 bytes should go in 'field4'
        0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
      };

      ExampleClass exampleClass = new ExampleClass();
      // Perform copy
    }
  }
}


0 commentaires

7 Réponses :


3
votes

Je pense que vous voulez que cela:

ici En modifiant le tampon dans le champ4, vous modifiez également les 3 autres types spécifiques. P>

[StructLayout(LayoutKind.Explicit, Size=8)]
struct UValue
{
   [FieldOffset(0)]
   public fixed byte fieldload[38]; // modify this to modify others

   [FieldOffset(0)]
   public uint64 field1;

   [FieldOffset(8)]
   public Uint32 field2

   [FieldOffset(12)]
   public Uint16 field3

   [FieldOffset(14)]
   public fixed byte field4[18]; // 18 bytes long
}


2 commentaires

Il est important de savoir que le mot clé fixe ne peut être utilisé que dans un contexte dangereux. Correction signifie littéralement que le champ est en réalité de type octet * au lieu d'un octet [].


@Christopher - Ceci est correct, il aurait peut-être même besoin d'un modificateur dangereux pour le pont de structure. Je n'ai pas essayé de compiler ce code.



3
votes

Une autre option, si vous pouvez utiliser une structure, consiste à reprocher à la matrice d'octet directement dans la structure.

var handle = GCHandle.Alloc(exampleData, GCHandleType.Pinned);
var structure = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof (ExampleStruct));
handle.Free();


3 commentaires

Neat --- afin de copier les données de votre copie pour gérer avant de le libérer? (Pouvez-vous donner un exemple de cela?)


@HOGAN: Le but du gychandle est de broder l'octet [] de sorte que le CLR / GC ne le déplace pas tandis que marshal.ptrtostructure fonctionne, et aussi nous pouvons obtenir l'adresse où le Octet [] vit. Le gratuit est pour le Gchandle lui-même. ALLOC ne copie pas le contenu de l'octet [].


Je pense que vous voulez dire "typeof (examens d'examen)".



15
votes

Vous avez beaucoup d'options, celle qui s'avère que le mieux dépend généralement de ce que votre programme a besoin / peut gérer et exactement combien de vitesse vous voulez. Il existe de nombreux articles qui expliquent les différentes manières que vous pouvez remplir une classe ou une structure avec des données.

La sérialisation binaire est une solution. Cela nécessite que vous écrivez un sérialiseur personnalisé, qui est assez facile. Un Article MSDN concernant ce montre que ce n'est pas trop difficile.

Vous pouvez écrire un constructeur privé qui prend dans la matrice d'octet et utilise un BinaryReader ou le bitconverter classe à Lire à partir de la matrice d'octet (vous devez l'envelopper dans un MemoryReam pour BinaryReader ) ou pour convertir simplement des sections du réseau d'octets aux valeurs dont vous avez besoin ( Bitconverter ). Dans le cas de bitconverter , vous auriez également besoin d'utiliser tampon.blockcopy pour copier les données restantes du tableau d'octets sur le champ Array d'octet de votre classe < P> Un troisième moyen, généralement généralement le moyen le plus rapide, est de convertir votre classe en une structure et d'utiliser un code dangereux pour lancer la matrice d'octets comme cette structure. Quelque chose comme ceci: xxx

Bien sûr, le code ci-dessus n'est valide que si vous pouvez utiliser un code dangereux. En outre, la conversion de la classe en structure peut également ne pas être ce que vous recherchez. Dans la plupart des cas, les structures sont transmises par une valeur aux fonctions, de sorte que les méthodes d'appel copie toute la structure (dans ce cas 32 octets) au lieu de transmettre une référence (4 ou 8 octets, selon l'architecture de la CPU) et peuvent Réduisez l'efficacité de votre programme. Il existe des exceptions aux structures étant transmises par la valeur et vous pouvez utiliser votre moteur de recherche préféré pour cela.

Si vous ne pouvez pas utiliser de code dangereux, il existe également marshal.ptrtostructure qui fera la même chose que le code ci-dessus, mais environ 10 fois plus lent. Vous auriez également besoin d'utiliser l'attribut marshalas pour spécifier la taille de la matrice, au lieu d'utiliser le mot-clé fixe (ce qui est dangereux). À ce stade, vous pouvez aussi bien utiliser le BinaryReader / Bitconverter, car il sera plus rapide que la classe de maréchal.


9 commentaires

+1, bonne réponse, comment ces manières se comparent-elles à utiliser layoutkind.Explicit comme dans mon exemple? (ou y a-t-il une raison pour laquelle je ne fonctionnera pas?)


Vous ne compilera pas, car byte [n'importe quel nombre] Varname n'est pas valide C # Code C #. En outre, Structlayout est destiné à laisser le marshallateur connaître la commande et / ou les compensations des champs de la structure. J'ai remarqué que vous avez spécifié le même décalage pour deux valeurs, et je pense que le marshaller lancera une erreur pour cela. Si vous souhaitez utiliser des attributs pour spécifier des tailles de tableau, vous pouvez utiliser l'attribut Marshalas et l'avoir du maréchal en tant que UNMANGEDTYPE.BYVALARRAY avec Sizeconst la taille de la matrice.


Même offset est valide, voir la documentation Microsoft ici: MSDN .microsoft.com / fr-US / US / Bibliothèque / AA288471 (v = vs.71) .aspx


@Hogan, tu as raison. C'est ce que je reçois pour aller de la mémoire :) de toute façon, la chose importante, c'est que l'utilisation de l'attribut marshalas serait mieux que de spécifier explicitement la mise en page dans ce cas. La spécification de la mise en page ne permettra pas au Marshaller de prendre en compte la taille de la matrice, sauf si vous n'utilisez pas de code dangereux.


J'aime cet Système non sécurisé Exampleclass idée. Cela fonctionne mais j'ai besoin de le convertir en octets. Y a-t-il un moyen similaire de faire ça?


fixe (octet * pb = & data [0]) == fixe (octet * pb = données)


"Parce que les structures sont immuables" - non, c'est une recommandation d'utilisation (discutable) uniquement.


@Renniepet Vous avez raison, ce n'est pas une déclaration précise. Je n'avais pas l'intention de dire qu'ils sont immuables (évidemment des champs publics ne sont pas immuables), j'avais mal essayé de dire qu'ils sont adoptés par la valeur et, ainsi copiés (comme la moitié de la moitié de la phrase indique). Merci pour le commentaire. Je mettrai à jour le libellé.


Merci encore pour cette réponse - cela m'a fait conscience de la manière dont le code dangereux pourrait être utilisé pour mapper une structure sur un tableau d'octets. J'ai maintenant pris le concept un pas plus loin, car je voulais tous les deux lire et écrire les champs mappés dans la matrice d'octet. Voir la "réponse" que j'ai posté ci-dessous.



1
votes

oublier la chose d'efficacité et rendre votre code maintenu et lisible en premier. Si nécessaire, profiler et améliorer.

Quel est le problème avec l'utilisation de la classe BitConverter. xxx


1 commentaires

"Faites de votre code de premier ordre et lisible" - Eh bien, vous avez démontré exactement pourquoi votre solution suggérée n'est pas très bonne. Ce n'est pas lisible (il contient beaucoup de "nombres magiques"), et il n'est définitivement pas maintenu. Si je voulais ajouter un nouveau champ entre le champ1 et le champ2, il y a 90% de chances que je ferais mal.



1
votes

et maintenant pour quelque chose de complètement différent ...

Cela ne répond pas vraiment à la question de l'OP; Au lieu de cela, c'est quelque chose que j'ai préparé pour fournir un moyen de cartographier une structure C # sur une matrice d'octet et permettez aux champs individuels de la matrice d'octets sous-jacents à lire et à écrire. Il utilise un code dangereux et obtient un pointeur sur le tableau d'octets, puis le jette à un pointeur à la struct qui mappe les champs. p>

Cela peut ne pas être tout ce qui est efficace, mais présente l'avantage de fournir une cartographie symbolique des champs sans utiliser de "nombres magiques" pour les longueurs et les décalages des champs. Mais notez que cela ne fonctionnera pas pour l'échange de données avec un système qui utilise Big Endian au lieu de la faible représentation des données Endian. P>

Les méthodes d'accès au champ sont implémentées comme méthodes d'extension. Voir TestMethod () pour un exemple de comment les utiliser. P>

Ceci a été inspiré par la réponse de Christopher Currens - si vous trouvez cela utile, donnez-lui un upvote. P>

using System;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
   [StructLayout(LayoutKind.Sequential, Pack = 1)]
   unsafe struct ExampleStruct
   {
      internal const int CField4Length = 18;

      public UInt64 Field1;
      public UInt32 Field2;
      public UInt16 Field3;
      public fixed byte Field4[CField4Length];
   }

   static unsafe class ExampleStructExtensionMethods
   {
      public static UInt64 GetField1(this byte[] byteArray)
      {
         fixed (byte* byteArrayPointer = &byteArray[0])
         {
            return (*(ExampleStruct*)byteArrayPointer).Field1;
         }
      }

      public static UInt32 GetField2(this byte[] byteArray)
      {
         fixed (byte* byteArrayPointer = &byteArray[0])
         {
            return (*(ExampleStruct*)byteArrayPointer).Field2;
         }
      }

      public static UInt16 GetField3(this byte[] byteArray)
      {
         fixed (byte* byteArrayPointer = &byteArray[0])
         {
            return (*(ExampleStruct*)byteArrayPointer).Field3;
         }
      }

      public static byte[] GetField4(this byte[] byteArray)
      {
         fixed (byte* byteArrayPointer = &byteArray[0])
         {
            byte[] field4 = new byte[ExampleStruct.CField4Length];
            for (int i = 0; i < ExampleStruct.CField4Length; i++)
               field4[i] = (*(ExampleStruct*)byteArrayPointer).Field4[i];

            return field4;
         }
      }

      public static void SetField1(this byte[] byteArray, UInt64 field1)
      {
         fixed (byte* byteArrayPointer = &byteArray[0])
         {
            (*(ExampleStruct*)byteArrayPointer).Field1 = field1;
         }
      }

      public static void SetField2(this byte[] byteArray, UInt32 field2)
      {
         fixed (byte* byteArrayPointer = &byteArray[0])
         {
            (*(ExampleStruct*)byteArrayPointer).Field2 = field2;
         }
      }

      public static void SetField3(this byte[] byteArray, UInt16 field3)
      {
         fixed (byte* byteArrayPointer = &byteArray[0])
         {
            (*(ExampleStruct*)byteArrayPointer).Field3 = field3;
         }
      }

      public static void SetField4(this byte[] byteArray, byte[] field4)
      {
         if (field4.Length != ExampleStruct.CField4Length)
            throw new ArgumentException("Byte array must have length 18", "field4");

         fixed (byte* byteArrayPointer = &byteArray[0])
         {
            for (int i = 0; i < ExampleStruct.CField4Length; i++)
               (*(ExampleStruct*)byteArrayPointer).Field4[i] = field4[i];
         }
      }
   }

   class TestProgram
   {
      byte[] exampleData =
      {
        // These 8 bytes should go in 'field1'
        0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
        // These 4 bytes should go in 'field2'
        0x08,0x09,0x0A,0x0B,
        // These 2 bytes should go in 'field3'
        0x0C,0x0D,
        // These 18 * 1 bytes should go in 'field4'
        0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
      };

      public void TestMethod()
      {
         UInt64 field1 = exampleData.GetField1();
         UInt32 field2 = exampleData.GetField2();
         UInt16 field3 = exampleData.GetField3();
         byte[] field4 = exampleData.GetField4();

         exampleData.SetField1(++field1);
         exampleData.SetField2(++field2);
         exampleData.SetField3(++field3);
         exampleData.SetField4(new byte[ExampleStruct.CField4Length] 
           { 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42 });
      }
   }
}


0 commentaires

0
votes

Pour utiliser des cours au lieu de la structure, vous pouvez utiliser quelque chose comme ce qui suit. J'ai utilisé divers éléments des réponses ci-dessus et j'ai placé ensemble pour une classe haute performance. Si vous n'aimez pas tout le codage manuel des méthodes SERIALIZE / DEERIALIZE, vous pouvez facilement écrire un générateur de code qui irait bien via tous les champs / propriétés et émettre les méthodes appropriées. Voici le code:

public interface ISerializableClass
{
    int SerializableClassSize { get; }
    StreamInflateTest Deserialize(byte[] buffer, int startOffset);
    byte[] Serialize(byte[] buffer, int startOffset);
}


public class   StreamInflateTest : ISerializableClass
{
    private const int _classSize = 10;
    public float Float32Value { get; set; }
    public Int32 Int32Value { get; set; }
    public byte Byte8Value { get; set; }
    public bool IsOk0 { get; set; }
    public bool IsOk1 { get; set; }
    public bool IsOk2 { get; set; }
    public bool IsOk3 { get; set; }
    public bool IsOk4 { get; set; }

    public StreamInflateTest()
    {
    }

    public int SerializableClassSize { get { return _classSize; } }
    public StreamInflateTest(byte[] buffer, int startOffset)
    {
        Deserialize(buffer, startOffset);
    }

    public unsafe StreamInflateTest Deserialize(byte[] buffer, int startOffset)
    {
        fixed (byte* pb = &buffer[startOffset])
        {
            Float32Value = *(float*)pb;
            Int32Value = *(int*)(pb + 4);
            Byte8Value = pb[8];
            BitField8 bitfld = new BitField8(pb[9]);
            IsOk0 = bitfld.Bit0;
            IsOk1 = bitfld.Bit1;
            IsOk2 = bitfld.Bit2;
            IsOk3 = bitfld.Bit3;
            IsOk4 = bitfld.Bit4;
        }

        return this;
    }
    public unsafe byte[] Serialize(byte[] buffer, int startOffset)
    {
        fixed (byte* pb = &buffer[startOffset])
        {
            *(float*)pb = Float32Value;
            *(int*)(pb + 4) = Int32Value;
            pb[8] = Byte8Value;
            BitField8 bitfld = new BitField8(0)
            {
                Bit0 = IsOk0,
                Bit1 = IsOk1,
                Bit2 = IsOk2,
                Bit3 = IsOk3,
                Bit4 = IsOk4
            };
            pb[9] = bitfld.Value;
        }

        return buffer;
    }
}

public struct BitField8
{
    public byte Value;

    public BitField8(byte value)
    {
        Value = value;
    }

    public bool Bit0
    {
        get { return (Value & 0x01) != 0; }
        set
        {
            if (value)
                Value |= 0x01;
            else
                Value = (byte)(Value & 0xFE);  // clear the bit
        }
    }
    public bool Bit1
    {
        get { return (Value & 0x02) != 0; }
        set
        {
            if (value)
                Value |= 0x02;
            else
                Value = (byte)(Value & 0xFD);  // clear the bit
        }
    }
    public bool Bit2
    {
        get { return (Value & 0x04) != 0; }
        set
        {
            if (value)
                Value |= 0x04;
            else
                Value = (byte)(Value & 0xFB);  // clear the bit
        }
    }
    public bool Bit3
    {
        get { return (Value & 0x08) != 0; }
        set
        {
            if (value)
                Value |= 0x08;
            else
                Value = (byte)(Value & 0xF7);  // clear the bit
        }
    }
    public bool Bit4
    {
        get { return (Value & 0x10) != 0; }
        set
        {
            if (value)
                Value |= 0x10;
            else
                Value = (byte)(Value & 0xEF);  // clear the bit
        }
    }
    public bool Bit5
    {
        get { return (Value & 0x20) != 0; }
        set
        {
            if (value)
                Value |= 0x20;
            else
                Value = (byte)(Value & 0xDF);  // clear the bit
        }
    }
    public bool Bit6
    {
        get { return (Value & 0x40) != 0; }
        set
        {
            if (value)
                Value |= 0x40;
            else
                Value = (byte)(Value & 0xBF);  // clear the bit
        }
    }
    public bool Bit7
    {
        get { return (Value & 0x80) != 0; }
        set
        {
            if (value)
                Value |= 0x80;
            else
                Value = (byte)(Value & 0x7F);  // clear the bit
        }
    }

    public bool Set(bool value, int bitNo)
    {
        if (bitNo > 7 || bitNo < 0)
            throw new ArgumentOutOfRangeException();

        if (value)
            Value |= (byte)(0x01 << bitNo);
        else
            Value = (byte)(Value & ~(0x01 << bitNo));  // clear the bit

        return value;
    }
    public bool Get(int bitNo)
    {
        if (bitNo > 7 || bitNo < 0)
            throw new ArgumentOutOfRangeException();

        return ((Value >> bitNo) & 0x01) != 0;
    }
    public bool this[int bitNo]
    {
        get { return Get(bitNo); }
        set { Set(value, bitNo); }
    }
}


0 commentaires

0
votes

Ceci peut être utilisé pour maréchaler un tableau d'octets et faire pivoter l'ordre d'octets. Handy pour les messages réseau passés de C. envelopper vos structures dans une classe pour les transmettre par réf.

using System;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Net;

namespace ConsoleApp1
{
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    struct MarshalMe
    {
        private UInt16 _param1;
        private UInt32 _param2;
        private UInt16 _param3;
        private UInt16 _param4;
        public ushort Param1 { get => _param1;  }
        public uint Param2 { get => _param2;  }
        public ushort Param3 { get => _param3; }
        public ushort Param4 { get => _param4; }
    }

    class Program
    {

        static void Main(string[] args)
        {

            byte[] bytes = new byte[] {0x00, 0x03, 0x00, 0x00,  0x00, 0x04, 0x00, 0x05, 0x00, 0x00 };

            var metoo = rotateStruct<MarshalMe>(stamp<MarshalMe>(bytes));

            Console.WriteLine("{0}-{1}-{2}", metoo.Param1, metoo.Param2, metoo.Param3);

            Console.ReadKey();
        }

        private static T stamp<T>(byte[] bytes)
        {
            var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
            var structure = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
            handle.Free();

            return (T)structure;
        }

        private static T rotateStruct<T>(object value)
        {
            FieldInfo[] myFieldInfo;
            Type myType = typeof(T);
            // Get the type and fields of FieldInfoClass.
            myFieldInfo = myType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);

            foreach (var s in myFieldInfo)
            {
                if (s.FieldType.Name == "UInt16"){
                    UInt16 u16 = (ushort)s.GetValue(value);
                    u16 = (ushort)IPAddress.HostToNetworkOrder((short)u16);
                    s.SetValue(value,u16 );
                }
                else if(s.FieldType.Name == "UInt32")
                {
                    UInt32 u32 = (uint)s.GetValue(value);
                    u32 = (uint)IPAddress.HostToNetworkOrder((int)u32);
                    s.SetValue(value, (object)u32);
                }
                else if (s.FieldType.Name == "UInt64")
                {
                    UInt64 u64 = (ushort)s.GetValue(value);
                    u64 = (ulong)IPAddress.HostToNetworkOrder((long)u64);
                    s.SetValue(value, u64);
                }

            }

            return (T)value;
        }      

    }
}


0 commentaires