为什么DllImport for C bool as UnmanagedType.I1抛出但是作为字节工作

这是一个简单的C代码(VS 2013 C ++项目,“编译为C”):

typedef struct { bool bool_value; } BoolContainer; BoolContainer create_bool_container (bool bool_value) { return (BoolContainer) { bool_value }; } 

这是我的P / Invoke包装器

 public partial class NativeMethods { [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] public static extern BoolContainer create_bool_container([MarshalAs(UnmanagedType.I1)] bool bool_value); } 

以下是BoolContainer的托管版本:

首先是抛出MarshalDirectiveException: Method's type signature is not PInvoke compatible.

 public struct BoolContainer // Marshal.SizeOf(typeof(BoolContainer)) = 1 { [MarshalAs(UnmanagedType.I1)] // same with UnmanagedType.U1 public bool bool_value; } 

第二是工作:

 public struct BoolContainer // Marshal.SizeOf(typeof(BoolContainer)) = 1 { public byte bool_value; } 

测试代码:

 BoolContainer boolContainer = NativeMethods.create_bool_container(true); 

似乎DllImport忽略了返回结构中任何布尔成员的MarshalAs 。 有没有办法在管理声明中保持bool

作为返回类型的结构在互操作中非常困难,它们不适合CPU寄存器。 这是由本地代码处理的,调用者在其堆栈帧上为返回值保留空间并将指针传递给被调用者。 然后通过指针复制返回值。 一般来说,这是一个非常麻烦的function,不同的C编译器有不同的策略来传递该指针。 pinvoke marshaller假设MSVC行为。

这是一个问题,结构不是blittable ,换句话说,当它的布局不再与本机代码所需的非托管结构完全匹配时。 这需要额外的步骤将非托管结构转换为托管结构,相当于Marshal.PtrToStructure()。 例如,如果向结构中添加字符串字段,则会看到此情况。 这使得它不闪烁,因为必须将C字符串转换为System.String,您将得到完全相同的exception。

在你的情况下类似的问题, bool也是一个非常难以互操作的类型。 由于C语言没有类型而导致后来添加了高度不兼容的选项。 它是C ++和CLR中的1个字节,COM中的2个字节,C中的4个字节和winapi。 pinvoke marshaller选择了winapi版本作为默认版本,使其与BOOL相匹配,BOOL是pinvoke最常见的原因。 这迫使你添加[MarshalAs]属性,这也需要额外的工作,再次由Marshal.PtrToStructure()完成。

因此,pinvoke marshaller缺少的是对这一额外步骤的支持,即在调用后自定义封送返回值。 不确定这是设计约束还是资源约束。 可能两者,非blittable结构成员的所有权在非托管语言中完全没有指定,并且猜测它, 特别是作为返回值,很少很好。 所以他们很可能已经打电话说这项function的工作并不值得成功的可能性很小。 这当然是猜测。

使用byte代替是一个很好的解决方法。