Erreur non blittable sur un type blittable

J’ai cette structure et ce code:

[StructLayout(LayoutKind.Sequential, Pack = 8)] private class xvid_image_t { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public int[] ssortingde; // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] // public IntPtr[] plane; } public int decore() { xvid_image_t myStruct = new xvid_image_t(); myStruct.ssortingde = new int[4]; // can be commented out - same result GCHandle.Alloc(myStruct, GCHandleType.Pinned); // ... } 

Lorsque j’essaie de l’exécuter, je reçois une ArgumentException disant:

L’object contient des données non primitives ou non compressibles

Après avoir lu cette page MSDN disant

Les types complexes suivants sont également des types blittables:

  • Tableaux unidimensionnels de types blittables, tels qu’un tableau d’entiers. Toutefois, un type contenant un tableau variable de types pouvant être blittés n’est pas lui-même blittable.

  • Les types de valeur formatés qui ne contiennent que des types définissables (et les classes s’ils sont marshalés en tant que types formatés). Pour plus d’informations sur les types de valeur formatés, voir Marshaling par défaut pour les types de valeur.

Je ne comprends pas ce que je fais mal. Je ne veux pas seulement utiliser Marshal , mais aussi comprendre cela.

Donc ce que je veux réellement, c’est savoir:

  1. Pourquoi?
  2. Comment puis-je résoudre ça?
  3. La solution que vous proposez fonctionnera-t-elle également avec la ligne commentée dans la structure?

J’utilise .Net 4.5, mais une solution pour .Net 2.0 est également nécessaire.

L’object contient des données non primitives ou non compressibles

C’est le message d’exception que vous recevez. Vous vous concentrez sur la partie “non cessible” du message, mais ce n’est pas le problème. C’est la partie “non primitive” qui est en cause. Un tableau est un type de données non primitif.

Le CLR essaie de vous éviter des ennuis ici. Vous pouvez épingler l’object mais vous avez toujours un problème, le tableau ne sera pas épinglé. Un object n’est pas vraiment épinglé lorsqu’il contient des champs qui doivent également l’être.

Et vous avez un plus gros problème avec le UnmanagedType.ByValArray, qui nécessite une conversion structurelle. En d’autres termes, la présentation dont vous avez besoin est complètement différente de celle de l’object de classe managée. Seul le marshaller pinvoke peut effectuer cette conversion.

Vous pouvez obtenir ce que vous voulez sans utiliser pinvoke marshaller en utilisant des tampons de taille fixe, en utilisant le mot-clé fixed . Cela nécessite l’utilisation du mot clé unsafe . Faites-le ressembler à ceci:

  [StructLayout(LayoutKind.Sequential)] unsafe private struct xvid_image_t { public fixed int ssortingde[4]; } 

Notez que vous devez modifier la déclaration en un type struct . C’est maintenant un type de valeur, vous n’avez plus besoin d’utiliser GCHandle pour épingler la valeur lorsque vous en faites une variable locale. Assurez-vous que tout code non géré prenant la valeur de la structure, généralement par référence, ne stocke pas de pointeur sur la structure. Cela va exploser gravement et sans aucun doute possible. Le mot clé unsafe est approprié ici. Si le pointeur est stocké, vous devez alors octroyer la puce et utiliser Marshal.AllocHGlobal () et Marshal.StructureToPtr () pour garantir que le pointeur rest valide tant que le code non géré l’utilise.

Une limite gênante de .NET réside dans le fait que les seuls éléments qu’il reconnaît sont un object System.Array autonome et un System.Ssortingng , qui sont tous deux des types de référence. Il est possible que le code écrit en C # utilise un tableau fixed (comme le note Hans Passant), mais un tel type n’est pas reconnu par .NET lui-même et le code qui utilise des tableaux fixes n’est pas vérifiable. De plus, les tableaux fixes sont limités à la détention de primitives et ne sont pas accessibles par d’autres langages tels que vb.net.

Deux alternatives à l’utilisation d’un tableau fixe sont:

  • remplacez le tableau fixe par une combinaison de champs qui totalisent la taille appropriée (en utilisant N variables dans la plupart des cas, mais en remplaçant par exemple un caractère char[4] par un UInt32 , ou un caractère char[8] par un UInt64 ). Si le tableau n’est pas trop volumineux, vous pouvez définir (via couper / coller ou reflection) un ensemble de méthodes statiques qui prennent une structure par référence et lisent / écrivent le bon élément, puis créent un tableau de delegates pour appeler ces méthodes. .

  • remplacez la structure entière par un tableau, puis transmettez le premier élément de ce tableau en tant que paramètre ref . Cela peut être encore plus “dangereux” que d’utiliser un tableau fixed dans une structure, mais c’est le seul moyen que je connaisse dans vb.net pour obtenir la sémantique “passage par référence” avec une structure qui contient quelque chose qui doit vraiment être accessible sous forme de tableau.

Bien que je puisse comprendre que les tableaux de type valeur puissent avoir été considérés comme “source de confusion” (en particulier s’ils étaient automatiquement configurés en boîte automatique), il existe des endroits où ils auraient été l’approche sémantiquement correcte pour le stockage de tableau, tant du sharepoint la sémantique by-ref pour COM interop et aussi du sharepoint vue des méthodes supposées renvoyer un petit nombre de valeurs. Par exemple, dans System.Drawing2d , il existe une méthode qui renvoie la transformation graphique actuelle sous forme de float[6] ; autrement que par expérimentation, il n’y aurait pas de moyen clair de savoir si les modifications apscopes à ce tableau une fois renvoyé auraient une incidence sur ou pourraient affecter ou ne garantiraient rien d’autre. Si la méthode renvoyait un tableau de type valeur, il serait clair que les modifications apscopes au tableau renvoyé ne peuvent affecter rien d’autre. Néanmoins, que les tableaux de type valeur aient été une partie utile du Framework, il n’en rest pas moins que cela n’existe pas, que ce soit pour de bonnes ou de mauvaises raisons.

J’ai pris la réponse ci-dessous à partir de ce lien ( ici )

 SItuLongEmailMsg msg = newSItuLongEmailMsg(); // set members msg.text = new byte[2048]; // assign to msg.text int msgSize = Marshal.SizeOf(msg); IntPtr ptr = Marshal.AllocHGlobal(msgSize); Marshal.StructureToPtr(msg, ptr, true); byte[] dataOut = new byte[msgSize]; Marshal.Copy(ptr, dataOut, 0, msgSize);