带有 LayoutKind.Explicit 的布尔编组,这是按设计破坏还是失败? [英] Boolean Marshalling with LayoutKind.Explicit, Is this broken or failing as designed?

查看:28
本文介绍了带有 LayoutKind.Explicit 的布尔编组,这是按设计破坏还是失败?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,Boolean 类型据说有一个四字节值的默认 marshal 类型.所以下面的代码有效:

First of all the Boolean type is said to have a default marshal type of a four-byte value. So the following code works:

    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 将这些结构组合成一个联合"时:

Clearly these structures marshal independently just fine. The values are translated as expected. However, when we combine these structures into a "union" by declaring LayoutKind.Explicit like this:

    [StructLayout(LayoutKind.Explicit)]
    struct Broken
    {
        [FieldOffset(0)]
        public A a;
        [FieldOffset(0)]
        public B b;
    }

我们突然发现自己无法正确编组这些类型.以下是上述结构的测试代码以及​​它是如何失败的:

We suddenly find ourselves unable to correctly marshal these types. Here is the test code for the above structure and how it fails:

        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 != false && a.bValue1 == true && !true.Equals(a.bValue1))

It's very humorous to see this express as true: (a.bValue1 != false && a.bValue1 == true && !true.Equals(a.bValue1))

当然,这里更大的问题是 a.iValue2 != 4,而不是 4 已更改为 1(可能是重叠的布尔值).

Of course the bigger problem here is that a.iValue2 != 4, rather the 4 has been changed to 1 (presumably by the overlapped bool).

所以问题是:这是一个错误,还是按设计失败了?

背景:这来自 结构之间有什么区别使用 PInvoke 时包含 bool vs uint?

Background: this came from What is the difference between structures containing bool vs uint when using PInvoke?

更新:当您使用大整数值 (> 255) 时,这更加奇怪,因为只有用于布尔值的字节被修改为 1,因此将 b.bValue2 的 0x0f00 更改为 0x0f01.对于上面的 a.bValue1,它根本没有被翻译,0x0f00 为 a.bValue1 提供了一个错误的值.

Update: This is even stranger when you use large integer values (> 255) as only the byte that is used for the boolean is being modified to a 1, thus changing 0x0f00 to 0x0f01 for the b.bValue2. For a.bValue1 above it's not translated at all and 0x0f00 provides a false value for a.bValue1.

更新 #2:

解决上述问题的最明显和合理的解决方案是使用 uint 进行编组并改为公开布尔属性.用变通方法"真正解决问题是没有问题的.我主要想知道这是一个错误还是您所期望的行为?

The most obvious and reasonable solution to the above issue(s) is to use a uint for the marshalling and expose boolean properties instead. Really solving the issue with a 'workaround' is not at question. I'm mostly wondering is this a bug or is this the behavior you would expect?

    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; } } 
    }

推荐答案

它按设计工作.

这是正在发生的事情:

获取新的 int[] { 2, 4 } 并将其编组为 A、B、Broken 和 Broken2.最后一个与 Broken 相同,但字段顺序颠倒(先 b,然后 a).

Take the new int[] { 2, 4 } and lets marshal it into A, B, Broken, and Broken2. The last is the same as Broken, but with fields' order reversed (first b, then a).

如果我们将整数编组到这些结构中,我们会在内存中获得以下值:

If we marshal the ints into these structures we get the following values in memory:

  • 答:1、4
  • B:2、1
  • 损坏:2、1
  • 破碎2:1、4

所以发生的事情如下:

  • 编组器遇到布尔值时,其值为:bool = (original != 0);
  • 当有两个字段映射到同一个内存时,最后一个字段的规则获胜

所以对于 A,第一个 int 被转换为 1,对于 B,第二个 int 被转换为 1,对于 Broken,由于 B 是最后一个字段,因此适用其规则,因此第二个 int 被转换为 1.对于 Broken2 也是如此.

So for A, the first int gets converted to 1, for B, the second int gets converted to 1, for Broken, since B is the last field, its rules apply, and hence the second int gets converted to 1. Similarly for Broken2.

这篇关于带有 LayoutKind.Explicit 的布尔编组,这是按设计破坏还是失败?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆