在 C# 中封送布尔数组与封送单个布尔值(定义为 int)到 bool

Marshaling an array of boolean vs marshaling a single boolean (defined as int) to bool in C#

在 C API 中,我将 BOOL 定义如下

#ifndef BOOL
#define BOOL int

我有一个结构,其中有一个简单的 BOOL 成员和一个 BOOL 数组

struct SomeStruct
{
    BOOL    bIsSomething;
    BOOL    bHasSomething[5];
}

现在我发现,当我想转换整个结构时,我必须以不同的方式编组它们:

我用 I1 编组的单个 BOOL 和我必须用 I4 编组的固定长度数组(如果我不这样做,它们的结构大小将不匹配,我将这些结构的数组提取到 C# 中时会遇到问题):

    [StructLayout(LayoutKind.Sequential)]
    public struct SomenNativeStruct
    {
        [MarshalAs(UnmanagedType.I1)] 
        public bool bIsSomething;
        [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I4, SizeConst = 5)]
        public bool[] bHasSomething;
    }

我怀疑我做错了什么,因为我不确定为什么我需要根据我是将它作为固定大小的数组还是作为单个成员来不同地编组相同的类型。 如果我将它们全部编组为 I4 我得到一个 System.ArgumentException

An unhandled exception of type 'System.ArgumentException' occurred in SomeDll.dll    
Additional information: Type 'Namespace.Document+SomeNativeStruct' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.

bool 是一种难以互操作的类型。关于什么是布尔值有许多相互不兼容的定义,因此 bool 被认为是 non-blittable 类型 - 也就是说,它需要真正编组,而不是仅仅将 "totally a bool" 标记粘贴到数据。 non-blittable 类型的数组是 doubly-tricky.

最简单的解决方案是完全避免使用 bool。只需将 bool[] 替换为 int[],并且假设原始类型实际上是 32 位 int(取决于编译器和平台),您将获得正确的互操作。然后,您可以手动将互操作结构复制到具有更合理布局的托管结构,如果您愿意的话 - 这也使您可以完全控制解释哪些 int 值分别对应于 true 和 false。

总的来说,原生互操作总是很棘手;您需要很好地理解实际的内存布局以及您正在处理的值和类型的含义。类型还不够——它们太模糊了,尤其是在标准 C 中(即使在今天,它通常是 the 本机互操作的标准)。 Headers 还不够 - 您还需要文档,甚至可能需要查看(本机)调试器。

额外的危险来自于这样一个事实,即没有安全网可以告诉您您正在做的事情 有点 错误 - 错误的互操作方法似乎可以工作多年,然后突然在你的脸上爆炸,例如。 true 值恰好是 42 而不是更常见的 -1,并且您的按位算术巧妙地中断(这个 can 实际上发生在 C# 中,如果您使用不安全的代码)。对于小于 32768 的值,一切都可能工作得很好,然后就会崩溃。有很多难以发现的错误情况,因此您需要格外小心。