En primer lugar, se dice que el tipo booleano tiene un tipo de mariscal predeterminado de un valor de cuatro bytes. Por lo tanto, el siguiente código funciona:Boolean Marshalling con LayoutKind.Explicit, ¿está roto o fallando según lo diseñado?
struct A
{
public bool bValue1;
public int iValue2;
}
struct B
{
public int iValue1;
public bool bValue2;
}
public static void Main()
{
int[] rawvalues = new int[] { 2, 4 };
A a = (A)Marshal.PtrToStructure(GCHandle.Alloc(rawvalues, GCHandleType.Pinned).AddrOfPinnedObject(), typeof(A));
Assert.IsTrue(a.bValue1 == true);
Assert.IsTrue(a.iValue2 == 4);
B b = (B)Marshal.PtrToStructure(GCHandle.Alloc(rawvalues, GCHandleType.Pinned).AddrOfPinnedObject(), typeof(B));
Assert.IsTrue(b.iValue1 == 2);
Assert.IsTrue(b.bValue2 == true);
}
Claramente estas estructuras marcan independientemente bien. Los valores se traducen como se esperaba. Sin embargo, cuando combinamos estas estructuras en una "unión" declarando LayoutKind.Explicit así:
[StructLayout(LayoutKind.Explicit)]
struct Broken
{
[FieldOffset(0)]
public A a;
[FieldOffset(0)]
public B b;
}
de repente nos encontramos incapaces de formar correctamente este tipo. Aquí está el código de prueba para la estructura anterior y cómo se produce un error:
int[] rawvalues = new int[] { 2, 4 };
Broken broken = (Broken)Marshal.PtrToStructure(GCHandle.Alloc(rawvalues, GCHandleType.Pinned).AddrOfPinnedObject(), typeof(Broken));
Assert.IsTrue(broken.a.bValue1 != false);// pass, not false
Assert.IsTrue(broken.a.bValue1 == true);// pass, must be true?
Assert.IsTrue(true.Equals(broken.a.bValue1));// FAILS, WOW, WTF?
Assert.IsTrue(broken.a.iValue2 == 4);// FAILS, a.iValue1 == 1, What happened to 4?
Assert.IsTrue(broken.b.iValue1 == 2);// pass
Assert.IsTrue(broken.b.bValue2 == true);// pass
Es muy cómico ver esto expresa como verdadero: (a.bValue1 = false & & a.bValue1 == true & & verdad! .Equals (a.bValue1))
Por supuesto, el problema más grande aquí es que a.iValue2! = 4, más bien el 4 ha sido cambiado a 1 (presumiblemente por el bool superpuesto).
Entonces la pregunta: ¿Es esto un error, o simplemente falló como se diseñó?
Antecedentes: esto vino de What is the difference between structures containing bool vs uint when using PInvoke?
Actualización: Esto es aún más extraño cuando se utiliza los valores de número entero grande (> 255), ya que sólo el byte que se utiliza para el booleano está siendo modificado a un 1, por lo tanto cambiando 0x0f00 a 0x0f01 para el b.bValue2. Para a.bValue1 anterior, no está traducido en absoluto y 0x0f00 proporciona un valor falso para a.bValue1.
Actualización # 2:
La solución más obvia y razonable a la cuestión (s) anteriormente es utilizar un uint para el cálculo de referencias y exponer propiedades booleanas en su lugar. Realmente resolver el problema con una 'solución alternativa' no está en duda. Me pregunto si esto es un error o es este el comportamiento que esperarías?
struct A
{
private uint _bValue1;
public bool bValue1 { get { return _bValue1 != 0; } }
public int iValue2;
}
struct B
{
public int iValue1;
private uint _bValue2;
public bool bValue2 { get { return _bValue2 != 0; } }
}
Me resulta interesante que la evaluación de "broken.a.bValue1 == true" es correcto cuando "true.Equals (broken.a.bValue1)" no lo es. Probaré lo de MarshalAs (UnmanagedType.Bool). –
usando MarshalAs (UnmanagedType.Bool) parece no tener ningún efecto. –
Pruebe esto: http://pastebin.ca/1665189 No hay nada impreso en el resultado de error una vez que comente el problema que ocurre debido a la IL anterior. – Gonzalo