2009-11-09 3 views
5

Прежде всего, тип 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; } } 
    } 

ответ

4

Он работает как разработанный.

Вот что происходит:

Возьмите новый ИНТ [] {2, 4} и позволяет мобилизовывать его в A, B, сломанная и Broken2. Последнее то же, что и Broken, но с порядком обращений полей (сначала b, затем a).

Если мы маршалировать РАСЧ в эти структуры мы получаем следующие значения в памяти:

  • A: 1, 4
  • B: 2, 1
  • Разбитая: 2, 1
  • Broken2: 1, 4

Так что же происходит следующая:

  • Когда маршаллер сталкивается с булевым значением, его значение равно: bool = (original! = 0);
  • Когда есть два поля, которые отображаются в одной и той же памяти, правила последнего поля выиграть

Так что для А, первый INT преобразуется в 1, для В, второй INT преобразуется в 1 , для Broken, так как B - это последнее поле, применяются его правила, и, следовательно, второй int преобразуется в 1. Аналогично для Broken2.

1

Линия прокомментировала: «FAILS, WOW, WTF?» не выполняется из-за того, как выполняется логическое сравнение. Он сравнивает 2 до 1:

IL_007e: ldc.i4.1 
IL_007f: ldloca.s 3 
IL_0081: ldflda valuetype Test/A Test/Broken::a 
IL_0086: ldfld bool Test/A::bValue1 
IL_008b: ceq 

Ceq заканчивает сравнение 1 к байту в bValue, который 2.

Самое смешное, что если (broken.a.bValue1) проверит 'true', потому что это отличное от нуля.

Что касается другой проблемы (broken.a.iValue2 == 4), он ушел, когда я применил:

[MarshalAs (UnmanagedType.Bool)] 

для обеих логических полей в структурах. Это гарантирует, что логические значения маршалируются как целое число (4 байта в .NET).

+0

Я просто нахожу это интересным, что оценка «broken.a.bValue1 == истинной» является правильным, когда «true.Equals (broken.a.bValue1)» нет. Я попробую MarshalAs (UnmanagedType.Bool). –

+0

, используя MarshalAs (UnmanagedType.Bool), кажется, не имеет никакого эффекта. –

+0

Попробуйте это: http://pastebin.ca/1665189 В сообщении об ошибке ничего не напечатано, как только я прокомментирую проблему, которая возникает из-за ИЛ выше. – Gonzalo

0

Казалось бы earlNameless правильно, так как добавление другой структуры Интс:

struct C 
    { 
     public int iValue1; 
     public int iValue2; 
    } 

до конца союза, кажется, исправить, по крайней мере часть проблемы. Однако это по-прежнему является недостатком, поскольку логическое значение будет учитывать однобайтовое значение и, как показано, не является надежным. Наконец, лучшим ответом, который я придумал, является использование специального типа для маршалинга.

[Serializable] 
[ComVisible(true)] 
public struct BOOL : IComparable, IConvertible, IComparable<BOOL>, IEquatable<BOOL>, IComparable<bool>, IEquatable<bool> 
{ 
    private uint _data; 

    public BOOL(bool value) { _data = value ? 1u : 0u; } 
    public BOOL(int value) { _data = unchecked((uint)value); } 
    public BOOL(uint value) { _data = value; } 

    private bool Value { get { return _data != 0; } } 
    private IConvertible Convertible { get { return _data != 0; } } 

    #region IComparable Members 
    public int CompareTo(object obj) { return Value.CompareTo(obj); } 
    #endregion 
    #region IConvertible Members 
    public TypeCode GetTypeCode() { return Value.GetTypeCode(); } 
    public string ToString(IFormatProvider provider) { return Value.ToString(provider); } 
    bool IConvertible.ToBoolean(IFormatProvider provider) { return Convertible.ToBoolean(provider); } 
    byte IConvertible.ToByte(IFormatProvider provider) { return Convertible.ToByte(provider); } 
    char IConvertible.ToChar(IFormatProvider provider) { return Convertible.ToChar(provider); } 
    DateTime IConvertible.ToDateTime(IFormatProvider provider) { return Convertible.ToDateTime(provider); } 
    decimal IConvertible.ToDecimal(IFormatProvider provider) { return Convertible.ToDecimal(provider); } 
    double IConvertible.ToDouble(IFormatProvider provider) { return Convertible.ToDouble(provider); } 
    short IConvertible.ToInt16(IFormatProvider provider) { return Convertible.ToInt16(provider); } 
    int IConvertible.ToInt32(IFormatProvider provider) { return Convertible.ToInt32(provider); } 
    long IConvertible.ToInt64(IFormatProvider provider) { return Convertible.ToInt64(provider); } 
    sbyte IConvertible.ToSByte(IFormatProvider provider) { return Convertible.ToSByte(provider); } 
    float IConvertible.ToSingle(IFormatProvider provider) { return Convertible.ToSingle(provider); } 
    ushort IConvertible.ToUInt16(IFormatProvider provider) { return Convertible.ToUInt16(provider); } 
    uint IConvertible.ToUInt32(IFormatProvider provider) { return Convertible.ToUInt32(provider); } 
    ulong IConvertible.ToUInt64(IFormatProvider provider) { return Convertible.ToUInt64(provider); } 
    object IConvertible.ToType(Type conversionType, IFormatProvider provider) { return Convertible.ToType(conversionType, provider); } 
    #endregion 
    #region IComparable<bool> Members 
    public int CompareTo(BOOL other) { return Value.CompareTo(other.Value); } 
    public int CompareTo(bool other) { return Value.CompareTo(other); } 
    #endregion 
    #region IEquatable<bool> Members 
    public bool Equals(BOOL other) { return Value.Equals(other.Value); } 
    public bool Equals(bool other) { return Value.Equals(other); } 
    #endregion 
    #region Object Override 
    public override string ToString() { return Value.ToString(); } 
    public override int GetHashCode() { return Value.GetHashCode(); } 
    public override bool Equals(object obj) { return Value.Equals(obj); } 
    #endregion 
    #region implicit/explicit cast operators 
    public static implicit operator bool(BOOL value) { return value.Value; } 
    public static implicit operator BOOL(bool value) { return new BOOL(value); } 
    public static explicit operator int(BOOL value) { return unchecked((int)value._data); } 
    public static explicit operator BOOL(int value) { return new BOOL(value); } 
    public static explicit operator uint(BOOL value) { return value._data; } 
    public static explicit operator BOOL(uint value) { return new BOOL(value); } 
    #endregion 
    #region +, -, !, ~, ++, --, true, false unary operators overloaded. 
    public static BOOL operator !(BOOL b) { return new BOOL(!b.Value); } 
    public static bool operator true(BOOL b) { return b.Value; } 
    public static bool operator false(BOOL b) { return !b.Value; } 
    #endregion 
    #region +, -, *, /, %, &, |, ^, <<, >> binary operators overloaded. 
    public static BOOL operator &(BOOL b1, BOOL b2) { return new BOOL(b1.Value & b2.Value); } 
    public static BOOL operator |(BOOL b1, BOOL b2) { return new BOOL(b1.Value | b2.Value); } 
    #endregion 
    #region ==, !=, <, >, <=, >= comparison operators overloaded 
    public static bool operator ==(BOOL b1, BOOL b2) { return (b1.Value == b2.Value); } 
    public static bool operator !=(BOOL b1, BOOL b2) { return (b1.Value != b2.Value); } 
    #endregion 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^