2 votes

FieldOffset doit-il être utilisé sur chaque membre de la classe/structure, si tant est qu'il soit utilisé ?

Prenons le cas où je dois m'assurer qu'une classe/structure est mappée en mémoire d'une manière très spécifique, probablement en raison de la nécessité de respecter un protocole externe :

[StructLayout(LayoutKind.Sequential, Pack=1)]
public class SYSTEM_INFO
{
 public ulong OemId;
 public ulong PageSize;
 public ulong ActiveProcessorMask;
 public ulong NumberOfProcessors;
 public ulong ProcessorType;
}

J'ai ensuite pensé à faire une "superposition" (est-ce un terme approprié ?) pour pouvoir accéder directement à la mémoire :

[StructLayout(LayoutKind.Explicit)]
public class SYSTEM_INFO
{
[FieldOffset(0)] public byte[] Buffer = new byte[40]; //overlays all the bytes, like a C union
[FieldOffset(0)] public ulong OemId;
[FieldOffset(8)] public ulong PageSize;
[FieldOffset(16)] public ulong ActiveProcessorMask;
[FieldOffset(24)] public ulong NumberOfProcessors;
[FieldOffset(32)] public ulong ProcessorType;
}

Mais cela prend beaucoup de temps et est source d'erreurs (si quelque chose change, je peux facilement me tromper en mettant à jour toutes les données de la base de données). FieldOffset ) - et il a été souligné qu'elle n'est en fait pas valable pour des raisons que je ne comprends pas entièrement :

Exception non gérée. System.TypeLoadException : Impossible de charger le type SYSTEM_INFO2' de l'assemblage 'a2bbzf3y.exe, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' parce qu'il contient un champ d'objet à l'offset 0 qui est incorrectement aligné ou recouvert par un champ champ non-objet. Commande terminée par le signal 6

Est-il possible de combiner les deux approches ? Buffer recouvre les membres, mais tous les membres sauf Buffer sont alignés automatiquement, séquentiellement, sans remplissage ? Je n'arrive pas à savoir, d'après la documentation, si cela est autorisé ou non, et je peux forcer le décalage de certains membres seulement, les autres étant décidés automatiquement.

2voto

Marc Gravell Points 482669

Dans le cas d'un struct vous pouvez le faire assez facilement - et franchement, si vous regardez les octets, vous devriez généralement utiliser un struct :

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

static class P
{
    static void Main()
    {
        var obj = new SystemInfo();
        var bytes = AsBytes(ref obj);
        Console.WriteLine(bytes.Length); // 40
    }

    static Span<byte> AsBytes<T>(ref T value)
        => MemoryMarshal.CreateSpan(
            ref Unsafe.As<T, byte>(ref value),
            Unsafe.SizeOf<T>());
}

public readonly struct SystemInfo
{
    public readonly ulong OemId;
    public readonly ulong PageSize;
    public readonly ulong ActiveProcessorMask;
    public readonly ulong NumberOfProcessors;
    public readonly ulong ProcessorType;
}

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