С# 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)
Ответы
Ответ 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/...