2009-11-24 2 views
10

Ich schreibe eine kleine C# -App, die einige Funktionen in einer C++ API aufruft. Ich habe den C++ Code, der in eine DLL aufbaut, und der C# Code ruft die API auf, die DllImport verwendet. (Ich verwende eine DEF-Datei für die C++ DLL so brauche ich nicht extern "C".)C++ aus C#: C++ - Funktion (in einer DLL), die falsch zurückgibt, aber C# denkt, dass es wahr ist!

Bisher ist die API hat eine Funktion, die derzeit absolut nichts:

bool Foo() 
{ 
    return false; 
} 

In C#, ich habe folgendes:

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

... 

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

Mein Problem ist, dass aus irgendeinem Grund, b immer TRUE auswertet. Ich habe einen Haltepunkt auf wenn (! B) und der Debugger meldet es als "wahr", unabhängig davon, was die C++ Funktion zurückgibt.

Ist das C# bool das gleiche wie das C++ bool? Auch wenn das nicht der Fall war, bekomme ich immer noch nicht, wie es den Rückgabewert als "wahr" empfinden würde :)

Kann mir jemand mit dieser bizarren Diskrepanz helfen?

Vielen Dank im Voraus!

+0

Wenn Sie die C++ Funktion return true, was hat Ihre C# Methode als Rückgabewert sehen? –

+0

Können Sie uns den Code zeigen, wo Sie die FooAPI.Foo() Methode verwenden? – Kevin

+1

Sollte Ihre C++ Funktion nicht extern "C" deklariert sein? –

Antwort

15

Versuchen Sie [return: MarshalAs (UnmanagedType.I1)]. Standardmäßig ist C# interop marshals C# bool as the Win32 BOOL, das ist das gleiche wie int, während C++ bool ist ein Byte AFAIR. Daher erwartet das Standard-Marshalling von C#, dass der Rückgabewert ein BOOL im eax-Register ist, und nimmt einige Nicht-Null-Einträge entgegen, da C++ bool in al zurückgegeben wird.

+0

Danke. Das macht viel mehr Sinn. Die Verwendung eines Boolean hat stattdessen den Trick gemacht. –

2

Ihr Code-Snippet wie gepostet kann nicht funktionieren. Wenn dies mit einem C++ - Compiler kompiliert wurde, wäre der Funktionsname "Foo @@ YA_NXZ" und Ihr C# -Code könnte ihn niemals finden. Die Aufrufkonvention ist ebenfalls wichtig, sie ist nicht der Standard (CallingConvention.StdCall), es sei denn, Sie sagen dem Compiler. Und irgendwo sollten Sie dem Linker gesagt haben, dass er die Funktion exportieren soll.

starten, indem Sie die exportierte Funktion deklarieren, damit es mit Standard-P kompatibel ist/Invoke-Attribute:

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

Nächstes Problem ist, dass die C++ Compiler nur ein Byte verwendet einen Bool zu speichern. Der Maschinencode für Ihre Rückkehr Anweisung generiert ist:

013D138E xor   al,al 

Die P/Invoke Einweiser wird jedoch davon ausgehen, es ist eine 32-Bit-Integer und prüfen Sie den Wert des EAX-Registers. Deklarieren Sie entweder den Rückgabetyp der Funktion als int oder BOOL oder verwenden Sie ein [return: MarshalAs (UnmanagedType.U1)] - Attribut in der C# -Deklaration.

+0

Entschuldigung, ich verwende eine .DEF-Datei anstelle von extern "C". Ich werde meinen ursprünglichen Beitrag bearbeiten. Wäre dies nicht der Fall gewesen, hätte sich C# über einen "fehlenden EntryPoint" beschwert. –

0

Ich benutzte unterzeichnet int.

C++ Code

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

C# -Code

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