2012-11-29 16 views
9

Penso di aver praticamente capito come scrivere i delegati C# per i callback, ma questo mi confonde. Il C++ definizione è la seguente:C# delegate per callback C++

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

e il mio C# approccio sarebbe:

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

Anche se questo sembra essere corretto, perché ricevo un errore di PInvokeStackInbalance, il che significa che la mia definizione del delegato è sbagliato.

Il resto dei parametri della funzione sono stringhe o int, il che significa che non possono causare l'errore, e se ho appena passato un IntPtr.Zero invece del delegato (il che significherebbe che sto puntando a un non- funzione di callback esistente) Ricevo un errore di AccessViolation, che ha anche un senso.

Cosa sto sbagliando?

EDIT: la funzione

La piena C++ è:

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

mio C# versioni:

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

La funzione è (per la prova) appena chiamato all'interno della routine principale un'applicazione console:

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

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

dove onCallback è questo:

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

ottengo l'errore PInvokeStackInbalance nella riga in cui chiamo _Initialize, se mi passa un IntPtr.Zero invece del delegato, e cambiare la definizione della funzione di IntPtr anziché CallbackDelegate poi Ottengo un AccessViolationException.

+0

'Il resto dei parametri della funzione sono stringhe o intarsi'. Non nascondere le informazioni. –

+1

Hai provato a non usare insicuro? i.e: delegate int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam); – DougEC

+0

@HansPassant Non sto cercando di nascondere le informazioni, stavo cercando di lasciare le cose che considero non rilevanti. Modificherò la domanda e aggiungerò le informazioni. – Valandur

risposta

3

A .NET long è 64 bit. Un C++ long può essere solo 32 bit. Verifica con il compilatore C++ che ha compilato la definizione di quale dimensione sono i long s.

+0

Buon input, purtroppo questo non sembra risolvere il problema. Non riesco a controllare il compilatore C++, dato che ho appena il .dll e il file .h, ma non è d'aiuto modificare il long in Int32. – Valandur

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

Se .NET assume cdecl invece di stdcall, lo stack verrà sicuramente protetto.

4

Ho aggiunto il codice al mio progetto corrente dove sto facendo un sacco di C#/C++ in VS2012. E mentre odio usare "ha funzionato sulla mia macchina", ha funzionato bene per me. Il codice che ho eseguito è elencato di seguito solo per illustrare che non ho apportato modifiche fondamentali.

Il mio suggerimento è quello di creare una nuova dll nativa con una funzione di stub per _Inizializzare come di seguito e vedere se funziona quando è possibile controllare entrambi i lati dell'interfaccia. Se funziona, ma la vera dll non lo fa, si tratta di impostazioni del compilatore sul lato nativo o di un bug sul lato nativo che si muove in pila.

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

Sul lato C#, ho aggiunto le dichiarazioni alla mia classe di interfaccia:

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); 

E poi nel mio principale:

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

Grazie per averlo verificato, avrei dovuto farlo io stesso. L'ho provato anche io e sembra funzionare. Quello che ho scoperto è che l'applicazione sembra funzionare se avvio il file exe, SENZA il debugger ... – Valandur

+0

È strano. Sembra che ci sia una buona possibilità che ci sia un bug nella dll lato C++ che sta interessando lo stack. C'è un modo per te di ottenere l'origine per la dll nativa in modo che tu possa eseguirli combinati sotto il debugger? Non conosco nessun altro buon modo per affrontarlo e non c'è quasi sicuramente modo di FISSARLO con la fonte. –

+0

No, purtroppo non riesco a mettere le mani sul codice sorgente, altrimenti penso che sarebbe molto più semplice. Ho provato a chiamare la funzione C++ da un programma C++, e questo sembra funzionare perfettamente bene. Perciò penso che la dll sia a posto, e sto solo facendo un errore con la funzione di callback. Grazie per l'aiuto però. – Valandur

Problemi correlati