Me gustaría obtener el desplazamiento de un campo en una estructura no administrada. Para esto uso el método Marshal.OffsetOf y me di cuenta que el resultado no refleja el Packing usado con StructLayout
Toma este ejemplo:
using System; using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential, Pack = 4)] unsafe public struct NETGROUP { public bool Advise; public bool Active; public int UpdateRate; public double DeadBand; } namespace MyApp // Note: actual namespace depends on the project name. { internal class Program { static void Main(string[] args) { unsafe { // Technique 1 (not correct) var oA = Marshal.OffsetOf(typeof(NETGROUP), "Advise").ToInt32();//0 var oB = Marshal.OffsetOf(typeof(NETGROUP), "Active").ToInt32();//4 var oC = Marshal.OffsetOf(typeof(NETGROUP), "UpdateRate").ToInt32();//8 var oD = Marshal.OffsetOf(typeof(NETGROUP), "DeadBand").ToInt32();//12 // Technique 2 (correct) NETGROUP ex = new NETGROUP(); byte* addr = (byte*)&ex; var oAa = (byte*)(&ex.Advise) - addr;//0 var oBb = (byte*)(&ex.Active) - addr;//1 var oCc = (byte*)(&ex.UpdateRate) - addr;//4 var oDd = (byte*)(&ex.DeadBand) - addr;//8 } } } }Necesito recuperar este desplazamiento en un constructor genérico pero la segunda técnica (que es la correcta) no me permite lograrlo sin especificar el tipo explícitamente
public CMember(Type type, string pszName, CType pType, uint uMod = Modifier.TMOD_NON, int nDim = 0) : base(DefineConstants.TOKN_MBR) { m_sName = pszName; m_nOffset = Marshal.OffsetOf(type, pszName).ToInt32(); // !!! m_pType = pType; m_uMod = uMod; m_nDim = nDim; }Tienes una idea ?
OffsetOf solo devuelve el diseño de las instancias no administradas de una estructura
OffsetOf proporciona el desplazamiento en términos del diseño de la estructura no gestionada, que no corresponde necesariamente al desplazamiento del diseño de la estructura gestionada. Ordenar la estructura puede transformar el diseño y alterar el desplazamiento.
Véase también StructLayout
Common Language Runtime controla el diseño físico de los campos de datos de una clase o estructura en la memoria administrada. Sin embargo, si desea pasar el tipo a código no administrado, puede usar el atributo StructLayoutAttribute para controlar el diseño no administrado del tipo.
'ex' es una instancia administrada, por lo que obtiene el diseño predeterminado
Marshal.OffsetOf funciona como se esperaba, el "problema" es System.Boolean , que es un tipo no blittable y tiene un tamaño no administrado de 4:
Console.WriteLine(sizeof(bool)); // 1 Console.WriteLine(Marshal.SizeOf(typeof(bool)));// prints 4 De los documentos de enumeración de UnmanagedType :
Bool Un valor booleano de 4 bytes (verdadero! = 0, falso = 0). Este es el tipo Win32 BOOL
Cambiar la estructura para que contenga campos de byte en lugar de bool produce el resultado esperado:
[StructLayout(LayoutKind.Sequential, Pack = 4)] unsafe public struct NETGROUP { public byte Advise; public byte Active; ... } Console.WriteLine(Marshal.OffsetOf(typeof(NETGROUP), "Advise")); // 0 Console.WriteLine(Marshal.OffsetOf(typeof(NETGROUP), "Active")); // 1 Otro enfoque es ordenar bool como UnmanagedType.I1 :
Un entero con signo de 1 byte. Puede utilizar este miembro para transformar un valor booleano en un booleano de estilo C de 1
bool(true= 1,false= 0).
[StructLayout(LayoutKind.Sequential, Pack = 4)] public struct NETGROUP { [MarshalAs(UnmanagedType.I1)] public byte Advise; [MarshalAs(UnmanagedType.I1)] public byte Active; ... }Algo más de información aquí .