Подтвердить что ты не робот

С# DllImport с булевой функцией С++ не возвращается правильно

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

Внутри моего класса С# у меня есть следующее:

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

Тем не менее, всякий раз, когда я вызываю свою функцию, ALWAYS возвращает true, даже когда я прокомментировал свою маленькую функцию и сделал ее возвратом false. У меня такое ощущение, что что-то не так с моей конвенцией о вызове или с какой-либо другой проблемой с P/Invoking моей DLL, вероятно, соответствует строке и const char *, но пока я совершенно не знаю. Что я делаю не так? Почему он возвращает true вместо false?

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

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

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

4b9b3361

Ответ 1

Я нашел решение для вашей проблемы. Ваше объявление должно предшествовать этому маршалингу: [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 (как некоторые из вас сказали), а С++ определяет его как 1 байт. Команда С# решила использовать 4 байта bool по умолчанию во время PInvoke, потому что большая часть функции системного API использует 4 байта в качестве bool. Если вы хотите изменить это поведение, вам нужно сделать это с помощью маршалинга, указав, что вы хотите использовать 1 байт.

Ответ 2

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

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

Это говорит о том, что UnmanagedType.Bool - это Win32 bool, который является int.

Ответ 3

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

[MarshalAs(UnmanagedType.LPStr)]

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

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

Ответ 4

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

Ответ 5

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

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

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

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

Ответ 6

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

__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;
    }
}

В С# я делаю

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

Ответ 7

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

fooobar.com/info/214601/...