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

C# DllImport with C++ boolean function not returning correctly

我在C++ DLL中有以下功能

1
2
3
4
5
6
7
8
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课程中,我有以下内容:

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

然而,每当我调用我的函数时,它总是返回真的,即使当我注释掉我的小函数并使它返回假的时候。我觉得我的调用约定有问题,或者P/Invoking my dll有其他问题,可能与字符串和const char*相对应,但现在我完全不知道了。我做错什么了?为什么它会返回真而不是假?

编辑:我发现这与const char*或字符串无关,因为空函数仍然存在问题。我试过更改cdecl和stdcall之间的调用约定,但都不能正常工作。我还设法调试了我的DLL,它被正确调用,并且确实返回了false,但是一旦返回到c中,不知何故它是真的。更改字符集也没有效果。我已经确保每次都为我的C程序提供最新和正确版本的DLL,所以这也不应该是问题。再一次,当我实际上返回错误时,我完全不知道为什么结果是正确的。

伊迪丝2:索瑞德给我提了一个建议,可以解决另一个重要问题,请看我的评论。遗憾的是,它不能解决退货问题。

edit3:我得出的结论是,将exist(bool)的返回类型更改为(int)会使它突然返回正确的数字(true=1,false=0)。这意味着C++的布尔和C的布尔可能会有问题。我可以继续使用int作为bool,但这仍然不能解释最初的问题。也许有人能在这方面启发我?也许这与我使用x64的事实有关(尽管两个poject都编译为x86)


我找到了解决你问题的办法。您的声明应在此封送处理之前:6

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

1
2
3
[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'sbool实际上是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


这实际上是由于EAX没有完全被典型的C++代码清除,返回返回一个EDCOX1×0。EAX在输入函数时通常包含一些伪值,对于return false,编译器通常会发出xor al, al。这只清除EAX的LSB,并使C代码将产生的非零值解释为true,而不是false


也许整理函数的参数可能有助于:

1
[MarshalAs(UnmanagedType.LPStr)]

以下是声明的外观:

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


我使用带符号的int,它可以正确返回真/假。

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


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

1
2
3
4
5
6
7
8
9
10
11
__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,我做

1
2
3
4
5
6
7
8
9
10
11
12
13
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());
    }
}


我测试了您的代码,它为我返回了false。所以肯定还有别的事情发生。

您确定要正确重新编译DLL吗?尝试删除.dll并重新生成。

除此之外,一切似乎都是好的假设。默认情况下,封送处理将.NET字符串处理为const char*而不必使用封送属性来修饰它,不管该dll是编译为ansi还是unicode。

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