2009-11-24 7 views
10

Estoy escribiendo una pequeña aplicación de C# que llama a algunas funciones en una API de C++. Tengo el código de C++ en una DLL, y el código de C# llama a la API usando DllImport. (Estoy usando un archivo .def para el C++ DLL de modo que no necesito extern "C".)C++ de C#: la función C++ (en una DLL) devuelve falso, pero C# cree que es verdad.

Hasta ahora, la API tiene una función, que en la actualidad no hace absolutamente nada:

bool Foo() 
{ 
    return false; 
} 

En C#, tengo los siguientes:

public class FooAPI 
{ 
    [DllImport("Foo.dll")] 
    public static extern bool Foo(); 
} 

... 

bool b = FooAPI.Foo(); 
if (!b) 
{ 
    // Throw an exception 
} 

mi problema es que, por alguna razón, b está siempre evaluando a VERDADERO. Tengo un punto de interrupción en if (! B) y el depurador lo informa como 'verdadero', irrelevante de lo que devuelve la función C++.

¿El boletín C# es el mismo que el bool C++? Aunque incluso si este no era el caso, todavía no entiendo cómo encontraría el valor de retorno como 'verdadero' :)

¿Alguien puede ayudarme con esta extraña discrepancia?

¡Gracias de antemano!

+0

Si usted tiene la C++ retorno de la función cierto lo que hace su método C# ver como el valor de retorno? –

+0

¿Podría mostrarnos el código donde usa el método FooAPI.Foo()? – Kevin

+1

¿No debería declararse su función C++ externamente "C"? –

Respuesta

15

Pruebe [return: MarshalAs (UnmanagedType.I1)]. Por defecto, C# interop marshals C# bool as the Win32 BOOL, que es lo mismo que int, mientras que C++ bool es un byte AFAIR. Por lo tanto, la clasificación predeterminada de C# espera que el valor de retorno sea BOOL en el registro eax, y recupere algo de basura distinta de cero porque C++ bool se devuelve en al.

+0

Gracias. Eso tiene mucho más sentido. Usar un Boolean en cambio hizo el truco. –

2

Su fragmento de código publicado no puede funcionar. Si esto se compiló con un compilador de C++, el nombre de la función sería? Foo @@ YA_NXZ y su código C# nunca podría encontrarlo. La convención de llamadas también es importante, no es la predeterminada (CallingConvention.StdCall) a menos que se lo indique al compilador. Y en algún lugar debería haberle dicho al vinculador que exporte la función.

inicio declarando la función exportada por lo que es compatible con defecto P/Invoke atributos:

extern "C" __declspec(dllexport) 
bool __stdcall Foo() { 
    return false; 
} 

siguiente problema es que el compilador de C++ utiliza un solo byte para almacenar un bool. El código de máquina generado para su estado de retorno es: Sin embargo

013D138E xor   al,al 

El P/Invoke marshaller se asuma que es un número entero de 32 bits y comprobar el valor del registro EAX. O declare el tipo de devolución de la función como int o BOOL o use un atributo [return: MarshalAs (UnmanagedType.U1)] en la declaración de C#.

+0

Disculpas, estoy usando un archivo .DEF en lugar de extern "C". Editaré mi publicación original. Si este no hubiera sido el caso, C# se habría quejado de un "EntryPoint faltante". –

0

He usado firmado int.

código C++

extern "C" __declspec(dllexport) signed int TestDouble(double* output) 
    { 
     *output = 1.3; 
     return true; 
    } 

código C#

[DllImport("abc.dll", EntryPoint = "TestDouble", CallingConvention = CallingConvention.Cdecl)] 
public static extern bool TestDouble(out double output);