C#/C++ 混合编程 bool 返回值异常

C# 调用 C++ DLL 时 bool 返回值异常:Release 模式下 false 变 true

简介

在近期测试中,使用 C# 通过 P/Invoke 调用 C++ DLL 中的函数,返回值为 Bool 类型的数据存在一些致命异常。
当测试 return false 时,Debug 中运行正常,在 Release 中运行偶发接收到的值为 true。
偶发情况为使用 printf 打印一些文本与写入文件等一些理论上与返回结果毫无关联的事件,就会导致结果正确。

代码

1
2
3
4
5
6
EXPORT_DLL bool Test();

bool Test()
{
return Check();
}
1
2
[DllImport(dllName, EntryPoint = "Test")]
public static extern bool Test();

错误分析

字节分析

通常认为 bool 在 C++ 和 C# 中都是 true/false,应该完全一致,但实际上并不是。
C++ 在 MSVC 编译器中 bool 通常占 1 byte,表示:

1
2
false = 0x00
true = 0x01

C# P/Invoke bool 按照 Windows BOOL 处理,通常占 4 byte,表示:

1
2
false = 0x00000000
true = 0x00000001

为什么 Debug 正常,Release 异常

Debug 模式:优化关闭、栈初始化、寄存器状态更稳定
Release 模式:开启优化、寄存器复用、删除无用代码

为什么增加打印或日志后问题消失

加入了一些代码后,可能改变了:栈布局、寄存器使用、编译器优化结果;导致了 Release 下返回寄存器状态发生变化产生的碰巧修复。

解决方案

修改 C# Bool 返回值大小

此为最小修改,UnmanagedType.I1 表示按照 1 byte 读取返回值,对应 C++ 的 bool。

1
2
3
[DllImport(dllName, EntryPoint = "Test")]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool Test();

DLL 接口不要使用 bool,改用 int

bool 是编程语言类型,不同平台、不同语言对于 bool 的 ABI 定义可能不同。
对于跨语言的 DLL 调用,更推荐使用 int 代替 bool。

1
2
3
4
5
6
EXPORT_DLL int Test();

int Test()
{
return Check();
}
1
2
[DllImport(dllName, EntryPoint = "Test")]
public static extern int Test();