带有C ++布尔函数的C#DllImport无法正确返回

我在C ++ DLL中有以下function

extern "C" __declspec(dllexport) bool Exist(const char* name) { //if (g_Queues.find(name) != g_Queues.end()) // return true; //else // return false; return false; } 

在我的C#类中,我有以下内容:

 [DllImport("Whisper.dll", EntryPoint="Exist", CallingConvention=CallingConvention.Cdecl)] public static extern bool Exist(string name); 

然而,每当我调用我的函数时,它总是返回true,即使我注释掉了我的小函数并使其返回false。 我觉得我的调用约定或P / Invoking我的DLL的任何其他问题都有问题,可能与字符串和const char *相对应,但是现在我完全无能为力。 我究竟做错了什么? 为什么它返回true而不是false?

编辑:我已经发现这与const char *或字符串无关,因为问题仍然存在于空函数中。 我试过改变Cdecl和StdCall之间的调用约定,但都没有正常工作。 我还设法调试我的DLL,它被正确调用,确实返回false,但一旦回到C#,它就会成真。 更改CharSet也没有任何效果。 我已经确保每次都为我的C#程序提供最新版本的DLL,所以这不应该是一个问题。 再一次,当我实际上返回false时,我完全不清楚为什么结果是真的。

EDIT2: SOReader为我提供了一个修复另一个重要问题的建议,请参阅我的评论。 可悲的是,它并没有解决退货问题。

EDIT3:我已经得出结论,将Exist(bool)的返回类型更改为(int)突然使它返回正确的数字(true = 1,false = 0)。 这意味着C ++的bool和C#的bool之间可能存在问题。 我可以继续使用int作为bool,但这仍然无法解释原始问题。 也许其他人可以在这个上启发我? 也许这与我使用x64的事实有关(虽然两个poject都被编译为x86)

我找到了解决问题的方法。 您的声明应在此编组之前: [return:MarshalAs(UnmanagedType.I1)]

所以一切都应该是这样的:

 [DllImport("Whisper.dll", EntryPoint="Exist", CallingConvention=CallingConvention.Cdecl)] [return:MarshalAs(UnmanagedType.I1)] public static extern bool Exist([MarshalAs(UnmanagedType.LPStr)] string name); 

我用我非常简单的例子测试了它并且它工作了!

编辑
为什么会这样? C将bool定义为4字节int(正如你们所说的那样),C ++将其定义为1字节。 C#团队决定在PInvoke期间使用4字节bool作为默认值,因为大多数系统API函数使用4字节值作为bool。 如果要更改此行为,则必须使用编组指定要使用1个字节值。

C的bool实际上是int ,因为原始C语言中没有布尔类型。 这意味着如果C#的DLLImport被设计为与C代码互操作,那么他们将期望C#的bool对应于C的int 。 虽然这仍然无法解释为什么错误会成为现实,修复它应该可以解决问题。

http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.unmanagedtype.aspx

这表示UnmanagedType.Bool是Win32 BOOL ,它是一个int

这实际上是由返回bool典型C ++代码未完全清除EAX引起的。 在进入函数时,EAX通常包含一些虚假值,并且为了return false ,编译器通常会发出xor al, al 。 这样只清除EAX的LSB,并使C#代码将得到的非零值解释为true而不是false

也许整理函数的参数可能会有所帮助:

 [的MarshalAs(UnmanagedType.LPStr)]

声明应如下所示:

 [DllImport(“Whisper.dll”,EntryPoint =“存在”,CallingConvention = CallingConvention.Cdecl)]
         public static extern bool Exist([MarshalAs(UnmanagedType.LPStr)] string name);

我测试了你的代码,它为我返回false。 所以必须有其他事情发生。

您确定要正确地重新编译DLL吗? 尝试删除.DLL并进行重建。

除此之外,一切似乎都很好。 默认情况下,编组将处理.NET字符串为const char *,而不必使用Marshal属性对其进行修饰,无论DLL是编译为ANSI还是Unicode。

请参阅http://msdn.microsoft.com/en-us/library/s9ts558h.aspx#cpcondefaultmarshalingforstringsanchor5

我使用以下系统发送布尔变量

 __declspec(dllexport) const bool* Read(Reader* instance) { try { bool result = instance->Read(); bool* value = (bool*)::CoTaskMemAlloc(sizeof(bool)); *value = result; return value; } catch (std::exception exp) { RegistryException(exp); return nullptr; } } 

在C#中,我做到了

 DllImport(WrapperConst.dllName)] public static extern IntPtr Read(IntPtr instance); public bool Read() { IntPtr intPtr = ReaderWrapper.Read(instance)); if(intPtr != IntPtr.Zero) { byte b = Marshal.ReadByte(intPtr); Marshal.FreeHGlobal(intPtr); return b != 0; } else { throw new Exception(GetLastException()); } } 

我使用signed int ,它可以正确返回true / false。

https://stackoverflow.com/a/42618042/1687981