2011-01-05 5 views
24

He la siguiente función en C++ DLLfunción booleana C# DllImport con C++ no volver correctamente

extern "C" __declspec(dllexport) bool Exist(const char* name) 
{ 
//if (g_Queues.find(name) != g_Queues.end()) 
// return true; 
//else 
// return false; 
return false; 
} 

Dentro de mi C# clase I tienen la siguiente:

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

Sin embargo, cada vez que llamo a mi funcionar SIEMPRE devuelve verdadero, incluso cuando comente mi pequeña función y la haga volver falsa. Tengo la sensación de que hay algo mal con mi convención de llamadas o cualquier otro problema con P/Invocar mi DLL, probablemente corresponde con la cadena y const char *, pero por ahora estoy completamente despistado. ¿Qué estoy haciendo mal? ¿Por qué se vuelve verdadero en lugar de falso?

EDIT: he dado cuenta de esto no tiene nada que ver con el char * const o cadena, porque el problema persiste con una función vacía. Intenté cambiar la convención de llamadas entre Cdecl y StdCall y tampoco funcionó correctamente. También he logrado depurar mi DLL y se está llamando correctamente y, de hecho, devuelve falso, pero una vez que vuelva a C#, de alguna manera es cierto. Cambiar el CharSet tampoco tuvo ningún efecto. Me he asegurado de haber suministrado mi programa C# con la versión más reciente y correcta de mi archivo DLL cada vez, así que eso tampoco debería ser un problema. Una vez más, no tengo ni idea de por qué el resultado es cierto cuando de hecho estoy devolviendo el nombre falso.

Edit2: SOReader me dio una sugerencia que fija otro tema importante, ver mi comentario. Lamentablemente, no soluciona el problema de devolución.

Edit3: I han concluido que el cambio del tipo de retorno de existir (bool) en (int) de repente hace que devuelve el número correcto (true = 1, false = 0). Eso significaría que puede haber un problema entre C++ bool y C# 's bool. Puedo continuar usando un int como bool, pero eso aún no explicaría el problema original. ¿Tal vez alguien más puede iluminarme en este caso? Quizás tiene que ver con el hecho de que estoy usando x64 (aunque ambos pojects están compilados como x86)

+0

El primero que hay que comprobar es que la función es, de hecho, 'cdecl'. Si su makefile pasa 'Gz' o' Gr' al compilador, entonces la función anterior no es 'cdecl'. Agregue un '__cdecl' a su código C, o active el asistente de depuración administrado' pInvokeStackImbalance'. –

+0

No creo que se vincule si se especifican/Gr o/Gz. Buen punto sobre el Asistente de depuración administrada. –

+0

Lo probé y __fastcall no enlazará con/clr. Pero __stdcall (/ Gz) enlaces pero luego el punto de entrada Exist no se encuentra en el tiempo de ejecución ya que la firma de la función es diferente. –

Respuesta

37

He encontrado la solución a su problema. Su declaración debe ir precedido con este cálculo de referencias: [return:MarshalAs(UnmanagedType.I1)]

así que todo debería tener este aspecto:

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

he comprobado en mi ejemplo muy simple y funcionó!

EDIT
¿Por qué sucede esto? C define bool como 4 bytes int (como algunos de ustedes han dicho) y C++ lo define como 1 byte. El equipo de C# decidió usar 4 bytes bool como valor predeterminado durante PInvoke porque la mayoría de la función API del sistema utiliza valores de 4 bytes como bool. Si desea cambiar este comportamiento, debe hacerlo con cálculo de referencias que especifique que desea usar un valor de 1 byte.

+0

Eso es extraño.Lo probé en VS 2010 con compilaciones de 32 bits de la aplicación DLL y C#, Y compilaciones de 64 bits de ambos y no tengo el problema descrito por Shammah. Lo que dices tiene sentido en una máquina Big Endian, pero no en arquitecturas Little Endian como los escritorios basados ​​en x86 que usamos. Recuerde, el byte menos significativo, el 1 o 0, en ambos casos para bool y 4 byte int sería el mismo. Probablemente es por eso que funciona perfectamente para mí. –

+0

@SimonBrangwin: a menos que desee leer el ensamblado generado o sea un vendedor del compilador, es prácticamente imposible comprender qué sucede cuando el tipo de devolución es incorrecto. Simplemente no funciona. Hay una razón por la cual C++ y C marcarían esto como un comportamiento indefinido. – Puppy

+0

Acepto su razonamiento re: C bools es de 4 bytes y C++ bools es de 1 byte, pero esto también ocurre cuando CallingConvention está configurado en ThisCall, lo que debería dejar en claro que se trata de un método de C++. Aún así, usted es solo el mensajero, y después de todo, su solución solucionó mi problema, ¡así que gracias! – ulatekh

0

Probé tu código y me devuelve falso. Así que debe haber algo más pasando.

¿Está seguro de que está recompilando la DLL correctamente? Intente eliminar el .DLL y hacer una reconstrucción.

Aparte de eso, todo parece estar bien asumiendo. De forma predeterminada, el marshalling manejará la cadena .NET para const char * sin tener que decorarla con atributos de Marshal, ya sea que la DLL esté compilada como ANSI o Unicode.

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

2

Tal vez el cálculo de referencias del argumento de la función podría ayudar:

 
[MarshalAs(UnmanagedType.LPStr)] 

Así es como la declaración debe verse como:

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

Al depurar esto, descubrí que esto debería utilizarse. Si mi cadena era "my_name", solo se pasaría "m" como argumento sin la clasificación. Con la clasificación, todo el "mi_nombre" pasará. Lamentablemente, esto todavía no soluciona el problema de retorno :( – Shammah

+0

He revisar mi proyecto escribió hace unos años y me pareció que utilicé cuando PreserveSig Bools donde volvieron Prueba esto y quiero saber si funciona:. [PreserveSig] bool foo(); – SOReader

+0

En realidad solo usé funciones de interfaz, no estáticas, así que no sé si mi punta funcionará; ( – SOReader

4

de C bool es en realidad int, como no hay ningún tipo booleano en el lenguaje C original. Eso significa que si DLLImport de C# está diseñado para interoperar con código C, entonces esperarán que C# 's bool corresponda a C int.Si bien esto aún no explica por qué lo falso se convertiría en realidad, corregirlo debería solucionar el problema.

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

Esto dice que UnmanagedType.Bool es el Win32 BOOL, que es un int.

2

Esto es causado por EAX no ser totalmente limpiado por el código C++ típico que devuelve un bool. Es típico que EAX contenga algún valor falso al ingresar una función, y para return false el compilador típicamente emitiría xor al, al. Esto borra solo el LSB de EAX y hace que el código C# interprete el valor resultante distinto de cero como true en lugar de false.

0

que enviar la variable booleana, usando el siguiente sistema

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

En C#, hago

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