知道了原因,解决方案自然就出来了,在Managed侧强制声明System.Boolean字段封送到Unmanaged侧时仅使用一个字节:
[MarshallAs(UnmanagedType.U1)]public bool TrueOrFalse;
坑二:字节对齐
对于C++开发者来说,可能知道当一个数据结构(class or struct)中的各字段在内存中进行排列时,会按照一个设定的装箱长度进行字节对齐,例如:
struct MyStruct { int one; short two; int three; bool four;}
假设在我们的平台上,sizeof(int)=4, sizeof(short)=2, sizeof(bool)=1, 如果问你sizeof(MyStruct)=?,你可能会马上做个加法得到答案,但是答案不一定对。It depends! 假设我们是按照4个字节对齐,这上面的结构体在内存中实际排列如下图:
了解这个对于我们编码有两个意义:
1、通过合理排列字段声明顺序来优化存储效率,内存布局中不留空洞;
2、MarshalAsAttribute支持Layout.Explicit来进行绝对定位,懂得了字节对齐可以配合Unmanaged侧的内存排列规则以保证字段长度映射正确,不然同样会发生字段长度不一致带来的困扰。
坑三:如何避免Double Free
Standard Marshalling Service/Interop marshaller总是试图释放Unmanaged侧代码分配的内存9,这会带来Double Free的问题,如果碰到这种问题,程序就会直接崩溃。
引用资料中举了以下例子:
BSTR MethodOne (BSTR b) { return b; }
如果这段代码直接从Unmanaged侧DLL中直接执行,不会发生任何额外的内存释放;但是当你从Managed侧调用这个方法时,b会被释放两次。
而更让人抓狂的是,并没有相应的信息提示究竟是哪个指针,哪个字段被DoubleFree了,你唯一能做的就是一点点加代码来验证自己猜测。所以,严格来说,并没有一个万无一失的方案来避免DoubleFree,你唯一能做的就是通过测试来验证结果(有点盲拧魔方的味道了)。