C # DllImport с булевой функцией C ++ не возвращается правильно - PullRequest
28 голосов
/ 05 января 2011

У меня есть следующая функция в 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?

РЕДАКТИРОВАТЬ: Я понял, что это не имеет ничего общего с const char * или строкой, потому что проблема сохраняетсяс пустой функцией.Я попытался изменить соглашение о вызовах между Cdecl и StdCall, и ни один из них не работает правильно.Мне также удалось отладить мою DLL, и она вызывается правильно и действительно возвращает false, но однажды вернувшись в C #, это как-то так.Изменение CharSet также не имело никакого эффекта.Я позаботился о том, чтобы каждый раз поставлял своей программе на C # последнюю и правильную версию своей DLL, так что это не должно быть проблемой.Опять же, я совершенно не понимаю, почему результат верен, когда я на самом деле возвращаю ложь.

EDIT2: SOReader предоставил мне предложение, которое исправляет другоеважный вопрос, см. мой комментарий.К сожалению, это не решает проблему возврата.

EDIT3: Я пришел к выводу, что изменение типа возврата Exist (bool) на (int) внезапно делает еговерните правильное число (true = 1, false = 0).Это означало бы, что может быть проблема между bool в C ++ и bool в C #.Я могу продолжать использовать int как bool, но это все равно не объясняет исходную проблему.Может быть, кто-то еще может просветить меня об этом?Возможно, это связано с тем, что я использую x64 (хотя оба объекта скомпилированы как x86)

Ответы [ 7 ]

50 голосов
/ 07 января 2011

Я нашел решение вашей проблемы.Вашему объявлению должен предшествовать этот маршалинг: [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);

Я проверил его в своем очень простом примере, и он работал!1008 * РЕДАКТИРОВАТЬ
Почему это происходит?C определяет bool как 4 байта int (как некоторые из вас сказали), а C ++ определяет его как 1 байт.Команда C # решила использовать 4-байтовое значение bool по умолчанию во время PInvoke, потому что большинство функций системного API используют 4-байтовые значения в качестве значения bool.Если вы хотите изменить это поведение, вы должны сделать это с маршалингом, указав, что вы хотите использовать 1-байтовое значение.

5 голосов
/ 06 января 2011

C bool на самом деле int, поскольку в исходном языке C нет логического типа.Это означает, что если DLL-импорте C # предназначен для взаимодействия с кодом C, то они будут ожидать, что C # bool будет соответствовать C * int.Хотя это по-прежнему не объясняет, почему false станет истинным, исправление должно решить проблему.

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

Это говорит о том, что UnmanagedType.Bool - это Win32 BOOL,int.

3 голосов
/ 17 января 2013

Это на самом деле вызвано тем, что EAX не полностью очищен типичным кодом C ++, который возвращает bool.Для EAX характерно содержать некоторые фиктивные значения при входе в функцию, и return false компилятор обычно будет выдавать xor al, al.Это очищает только младший бит EAX и заставляет код C # интерпретировать полученное ненулевое значение как true вместо false.

2 голосов
/ 06 января 2011

Возможно, может помочь маршалинг аргумента функции:

[MarshalAs(UnmanagedType.LPStr)]

Вот как должно выглядеть объявление:

[DllImport("Whisper.dll", EntryPoint="Exist", CallingConvention=CallingConvention.Cdecl)]
        public static extern bool Exist([MarshalAs(UnmanagedType.LPStr)] string name);
0 голосов
/ 06 марта 2017

Я использую со знаком int , который может правильно вернуть true / false.

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

0 голосов
/ 04 мая 2016

Я отправляю логическую переменную, используя следующую систему

__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 голосов
/ 05 января 2011

Я проверил ваш код, и он возвращает false для меня.Так что должно быть что-то еще происходит.

Вы уверены, что правильно перекомпилируете DLL?Попробуйте удалить .DLL и выполнить перестройку.

В остальном все выглядит нормально, если предположить.По умолчанию маршаллинг будет обрабатывать строку .NET в соответствии с const char * без необходимости украшать ее атрибутами Marshal, независимо от того, скомпилирована ли DLL как ANSI или Unicode.

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

...