首页 文章

具有C布尔函数的C#DllImport未正确返回

提问于
浏览
24

我在C DLL中有以下功能

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?

EDIT: 我已经发现这与const char *或字符串无关,因为问题仍然存在于空函数中 . 我've tried changing the calling convention between Cdecl and StdCall and neither work correctly. I' ve也设法调试我的DLL,它确保我也是一个问题 . 再一次,当我实际上返回false时,我完全不清楚为什么结果是真的 .

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

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

7 回答

  • 0

    我使用 signed int 可以正确返回true / false .

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

  • 5

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

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

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

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

  • 0

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

    __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());
        }
    }
    
  • 0

    我找到了解决问题的方法 . 您的声明应在此编组之前: [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);
    

    我在一个非常简单的例子中测试了它并且它工作了!

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

  • 3

    C的 bool 实际上是 int ,因为原始C语言中没有布尔类型 . 这意味着如果C#'s DLLImport is designed to interop with C code, then they will expect that C#' s bool 对应于C的 int . 虽然这仍然无法解释为什么错误会成为现实,但修复它应该可以解决问题 .

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

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

  • 44

    这实际上是由于EAX没有被返回 bool 的典型C代码完全清除 . 在进入函数时,EAX通常包含一些虚假值,并且编译器通常会发出 xor al, al . 这样只清除EAX的LSB,并使C#代码将结果非零值解释为 true 而不是 false .

  • 2

    也许编组函数的参数可能会有所帮助:

    [MarshalAs(UnmanagedType.LPStr)]
    

    以下是声明的外观:

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

相关问题