I have the following function in a C++ DLL
extern \"C\" __declspec(dllexport) bool Exist(const char* name)
{
//if (g_Queues.find(name) != g_Queues.end())
/
C'sbool
is actually int
, as there is no boolean type in the original C language. That means that if C#'s DLLImport is designed to interop with C code, then they will expect that C#'s bool
to correspond to C's int
. While this still doesn't explain why false would become true, fixing it should fix the problem.
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.unmanagedtype.aspx
This says that UnmanagedType.Bool is the Win32 BOOL
, which is an int
.
This is actually caused by EAX not being fully cleared out by typical C++ code that returns a bool
. It's typical for EAX to contain some bogus value when entering a function, and to return false
the compiler would typically emit xor al, al
. This clears out only the LSB of EAX, and causes C# code to interpret the resulting non-zero value as true
instead of false
.
I use signed int which can return true/false correctly.
https://stackoverflow.com/a/42618042/1687981
I found the solution for your problem. Your declaration should be preceded with this marshaling:
[return:MarshalAs(UnmanagedType.I1)]
so everything should look like this:
[DllImport("Whisper.dll", EntryPoint="Exist", CallingConvention=CallingConvention.Cdecl)]
[return:MarshalAs(UnmanagedType.I1)]
public static extern bool Exist([MarshalAs(UnmanagedType.LPStr)] string name);
I tested it in my very simple example and it worked!
EDIT
Why this happens? C defines bool as 4 bytes int (as some of you have said) and C++ defines it as 1 byte. C# team decided to use 4 byte bool as default during PInvoke because most of the system API function use 4 bytes values as bool. If you want to change this behavior you have to do it with marshaling specifying that you want to use 1 byte value.
Perhaps marshaling the argument of the function might help:
[MarshalAs(UnmanagedType.LPStr)]
Here is how the declaration should look like:
[DllImport("Whisper.dll", EntryPoint="Exist", CallingConvention=CallingConvention.Cdecl)] public static extern bool Exist([MarshalAs(UnmanagedType.LPStr)] string name);
I send the boolean variable, using the following system
__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;
}
}
In C #, I do
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());
}
}