Прежде всего, тип Boolean имеет тип маршала по умолчанию для четырехбайтового значения. Таким образом, работает следующий код:Boolean Marshalling with LayoutKind.Explicit, это сломанный или неудачный, как запроектировано?
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);
}
Очевидно, что эти структуры маршалируют независимо друг от друга просто отлично. Значения пересчитываются, как ожидалось. Однако, когда мы объединим эти структуры в «союз», объявляя LayoutKind.Explicit как это:
[StructLayout(LayoutKind.Explicit)]
struct Broken
{
[FieldOffset(0)]
public A a;
[FieldOffset(0)]
public B b;
}
Мы внезапно оказываемся не в состоянии правильно выстроить эти типы. Вот тестовый код для вышеуказанной структуры и как она терпит неудачу:
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
Это очень юмористическое видеть это экспресс, как верно: (a.bValue1 = ложь & & a.bValue1 == истинного & & правда! .Equals (a.bValue1))
Конечно, большая проблема здесь в том, что a.iValue2! = 4, а 4 был изменен на 1 (предположительно, наложенным bool).
Итак, вопрос: это ошибка, или просто не сработало, как было разработано?
фона: это произошло из What is the difference between structures containing bool vs uint when using PInvoke?
Update: Это еще более странно при использовании больших целочисленных значений (> 255), как только байт, который используется для логического значения модифицируется на 1, таким образом, изменяя 0x0f00 на 0x0f01 для b.bValue2. Для a.bValue1 выше он вообще не переведен, а 0x0f00 предоставляет ложное значение для a.bValue1.
Обновление # 2:
Наиболее очевидное и разумное решение данного вопроса (ами) заключается в использовании UINT для сортировочного и подвергать логические свойства вместо этого. На самом деле решение вопроса с «обходным путем» не под вопросом. Мне в основном интересно, это ошибка или это поведение вы ожидаете?
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; } }
}
Я просто нахожу это интересным, что оценка «broken.a.bValue1 == истинной» является правильным, когда «true.Equals (broken.a.bValue1)» нет. Я попробую MarshalAs (UnmanagedType.Bool). –
, используя MarshalAs (UnmanagedType.Bool), кажется, не имеет никакого эффекта. –
Попробуйте это: http://pastebin.ca/1665189 В сообщении об ошибке ничего не напечатано, как только я прокомментирую проблему, которая возникает из-за ИЛ выше. – Gonzalo