まず第一に、ブール型は4バイト値のデフォルトのマーシャル型を持っていると言われています。したがって、次のコードが機能します。
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
この表現をtrueと見なすのは非常にユーモラスです:(a.bValue1!= false && a.bValue1 == true &&!true.Equals(a.bValue1))
もちろん、ここでのより大きな問題は、a.iValue2!= 4であり、4が1に変更されていることです(おそらくブール値が重複しているため)。
だから質問:これはバグですか、それとも設計どおりに失敗しただけですか?
背景:これ は、PInvokeを使用する場合のboolとuintを含む構造の違いは何ですか?
更新:ブール値に使用されるバイトのみが1に変更され、b.bValue2の0x0f00が0x0f01に変更されるため、大きな整数値(> 255)を使用する場合はさらに奇妙になります。上記のa.bValue1の場合、それはまったく変換されず、0x0f00はa.bValue1にfalse値を提供します。
アップデート#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; } }
}