2012-11-29 7 views
9

Ich denke, ich habe im Grunde verstanden, wie C# Delegaten für Rückrufe zu schreiben, aber diese verwirrte mich. Die C++ Definition lautet wie folgt:C# Delegate für C++ Callback

typedef int (__stdcall* Callback)(
long lCode, 
long lParamSize, 
void* pParam 
); 

und mein C# Ansatz wäre:

unsafe delegate int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam); 

Obwohl dies nicht korrekt zu sein scheint, weil ich einen PInvokeStackInbalance Fehler, die meine Definition des Delegierten bedeutet ist falsch.

Der Rest der Parameter der Funktion sind Zeichenfolgen oder Ints, was bedeutet, dass sie den Fehler nicht verursachen können, und wenn ich nur ein IntPtr.Zero anstelle des Delegaten übergebe (was bedeuten würde, dass ich auf eine nicht Callback-Funktion) bekomme ich einen AccessViolation-Fehler, der auch Sinn macht.

Was mache ich falsch?

EDIT:

Die volle C++ Funktion ist:

int 
__stdcall 
_Initialize (
const char*   FileName, 
Callback   cbFunction, 
int     Code, 
const char*   Name, 
unsigned int  Option, 
unsigned int  Option2 
); 

Meine C# -Version ist:

[DllImport("MyDll.dll", CallingConvention = CallingConvention.StdCall)] 
public static extern int _Initialize (string FileName, CallbackDelegate cbFunction, int Code, string Name, uint Options, uint Options2); 

Die Funktion (zum Testen) ist gerade innerhalb der Hauptroutine aufgerufen eine Konsolenanwendung:

static void Main(string[] args) 
{ 
    CallbackDelegate del = new CallbackDelegate(onCallback); 

    Console.Write(_Initialize("SomeFile.dat", del, 1000, "", 0, 4)); 
    Console.Read(); 
} 

wo onCallback ist dies:

static int onCallback(int lCode, int lParamSize, IntPtr pParam) 
{ 
    return 0; 
} 

Ich erhalte die PInvokeStackInbalance Fehler auf der Linie, wo ich _Initialize nennen, wenn ich einen IntPtr.Zero anstelle des Delegaten übergeben, und die Definition der Funktion IntPtr ändern statt CallbackDelegate dann Ich bekomme eine AccessViolationException.

+0

'Der Rest der Parameter der Funktion sind Strings oder Ints'. Verstecken Sie keine Informationen. –

+1

Haben Sie versucht, nicht unsicher zu verwenden? d. h.: Delegat int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam); – DougEC

+0

@HansPassant Ich versuche nicht, Informationen zu verstecken, ich habe versucht, Dinge wegzulassen, die ich für nicht relevant hielt. Ich werde die Frage bearbeiten und die Informationen hinzufügen. – Valandur

Antwort

3

Ein .NET long ist 64Bits. Ein C++ long kann nur 32 Bit sein. Überprüfen Sie mit dem C++ - Compiler, der diese Definition kompiliert hat, welche Größe es long s ist.

+0

Gute Eingabe, Leider scheint dies das Problem nicht zu lösen. Ich kann den C++ - Compiler nicht überprüfen, da ich nur die .dll- und die .h-Datei habe, aber das Ändern der langen in Int32 hilft nicht. – Valandur

3
[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)] 
unsafe delegate int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam); 

Wenn .NET übernimmt cdecl statt stdcall Ihr Stack wird ganz gewiss abgespritzt werden.

4

Ich habe Ihren Code zu meinem aktuellen Projekt hinzugefügt, wo ich eine Menge C#/C++ - Interop in VS2012 mache. Und obwohl ich es hasse das "es funktionierte an meiner Maschine" zu benutzen, hat es für mich gut funktioniert. Der Code, wie ich ihn ausgeführt habe, ist unten aufgeführt, nur um zu verdeutlichen, dass ich keine grundlegenden Änderungen vorgenommen habe.

Mein Vorschlag ist, dass Sie eine neue native DLL mit einer Stub-Funktion für _Initialize wie unten erstellen und sehen, ob es funktioniert, wenn Sie beide Seiten der Schnittstelle steuern können. Wenn das funktioniert, aber die echte DLL nicht, kommt es entweder auf die Compiler-Einstellungen auf der nativen Seite oder sie ist ein Fehler auf der nativen Seite und stampft auf dem Stack.

extern "C" { 

    typedef int (__stdcall* Callback)(long lCode,long lParamSize,void* pParam); 

    TRADITIONALDLL_API int __stdcall _Initialize (const char* FileName,Callback cbFunction, int     Code, 
           const char*   Name,unsigned int  Option,unsigned int  Option2) 
    { 
     cbFunction(0, 0, nullptr); 
     return 0; 
    } 
} 

Auf der C# Seite, fügte ich die Erklärungen meiner Interface-Klasse:

public delegate int CallbackDelegate(int lCode, int lParamSize, IntPtr pParam); 

[DllImport("XXX.dll", CallingConvention = CallingConvention.StdCall)] 
public static extern int _Initialize(string FileName, CallbackDelegate cbFunction, int Code, string Name, uint Options, uint Options2); 

Und dann in meinem Haupt:

private int onCallback(int lCode, int lParamSize, IntPtr pParam) 
{ 
     return 0; 
} 
XXXInterface.CallbackDelegate del = new XXXInterface.CallbackDelegate(onCallback); 

Console.Write(XXXInterface._Initialize("SomeFile.dat", del, 1000, "", 0, 4)); 
+0

Vielen Dank für das Auschecken, sollte das selbst gemacht haben. Ich habe es auch versucht und es scheint zu funktionieren. Was auch herausgefunden haben ist, dass die Anwendung funktioniert, wenn ich nur die EXE-Datei starten, OHNE den Debugger ... – Valandur

+0

Das ist seltsam. Es scheint, dass es eine gute Chance gibt, dass es einen Fehler in der C++ - Seiten-DLL gibt, der den Stack beeinflusst. Gibt es eine Möglichkeit für Sie, die Quelle für die native DLL zu bekommen, so dass Sie sie unter dem Debugger kombiniert ausführen können? Ich kenne keinen anderen guten Weg, um es anzugehen und es gibt fast sicher keine Möglichkeit, es mit der Quelle zu FIXIEREN. –

+0

Nein, leider kann ich den Quellcode nicht in die Hände bekommen, sonst wäre das viel einfacher. Ich habe versucht, die C++ - Funktion von einem C++ - Programm aufrufen, und das scheint völlig in Ordnung zu sein. Daher denke ich, dass die DLL in Ordnung ist, und ich mache nur einen Fehler mit der Callback-Funktion. Danke für die Hilfe. – Valandur