Quindi ho una base di codice C++ di terze parti nativa Sto lavorando con file (.lib e .hpp) che ho usato per costruire un wrapper in C++/CLI per l'uso finale in C#.Violazione di accesso Eccezione/crash dalla richiamata C++ alla funzione C#
Ho incontrato un problema particolare quando si passa da Debug a Release, in quanto ottengo un'eccezione di violazione di accesso quando viene restituito un codice di callback.
il codice dal file HPP originali per il formato funzione di callback:
typedef int (*CallbackFunction) (void *inst, const void *data);
Codice dal C++/CLI wrapper per il formato funzione di callback: (ti spiego perché ho dichiarato due in un attimo)
public delegate int ManagedCallbackFunction (IntPtr oInst, const IntPtr oData);
public delegate int UnManagedCallbackFunction (void* inst, const void* data);
--Quickly, il motivo per cui ho dichiarato un secondo "UnManagedCallbackFunction" è che ho cercato di creare un callback "intermediario" nella confezione, in modo che la catena di cambiato da nativi C++> C# per una versione di Native C++> C++/CLI Wrapper> C# ... Full disclos Il problema persiste, è appena stato trasferito al C++/CLI Wrapper ora sulla stessa riga (il ritorno).
E, infine, il codice schiantarsi da C#:
public static int hReceiveLogEvent(IntPtr pInstance, IntPtr pData)
{
Console.WriteLine("in hReceiveLogEvent...");
Console.WriteLine("pInstance: {0}", pInstance);
Console.WriteLine("pData: {0}", pData);
// provide object context for static member function
helloworld hw = (helloworld)GCHandle.FromIntPtr(pInstance).Target;
if (hw == null || pData == null)
{
Console.WriteLine("hReceiveLogEvent: received null instance pointer or null data\n");
return 0;
}
// typecast data to DataLogger object ptr
IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataLoggerWrap(pData)));
DataLoggerWrap dlw = (DataLoggerWrap)GCHandle.FromIntPtr(ip2).Target;
//Do Logging Stuff
Console.WriteLine("exiting hReceiveLogEvent...");
Console.WriteLine("pInstance: {0}", pInstance);
Console.WriteLine("pData: {0}", pData);
Console.WriteLine("Setting pData to zero...");
pData = IntPtr.Zero;
pInstance = IntPtr.Zero;
Console.WriteLine("pData: {0}", pData);
Console.WriteLine("pInstance: {0}", pInstance);
return 1;
}
Tutto scrive alla console sono fatto e poi vediamo l'incidente temuta sul ritorno:
Eccezione non gestita a 0x04d1004c in helloworld.exe: 0xC0000005: accesso alla posizione di lettura della violazione 0x04d1004c.
Se io passo nel debugger da qui, tutto quello che vedo è che l'ultima voce sul stack di chiamate è il seguente:> "04d1004c()", che restituisce un valore decimale di: 80805964
Quale è solo interessante se si guarda alla console che mostra:
entering registerDataLogger
pointer to callback handle: 790848
fp for callback: 2631370
pointer to inst: 790844
in hReceiveLogEvent...
pInstance: 790844
pData: 80805964
exiting hReceiveLogEvent...
pInstance: 790844
pData: 80805964
Setting pData to zero...
pData: 0
pInstance: 0
Ora, so che tra il debug e rilasciare alcune cose sono molto diverse in tutto il mondo Microsoft. Ovviamente, sono preoccupato per il byte padding e l'inizializzazione delle variabili, quindi se c'è qualcosa che non sto fornendo qui, fammelo sapere e aggiungerò al post (già lungo). Penso inoltre che il codice gestito NON stia rilasciando tutta la proprietà e quindi il materiale nativo di C++ (di cui non ho il codice) potrebbe tentare di eliminare o uccidere l'oggetto pData, causando così il crash dell'applicazione.
Più informazioni complete, tutto funziona correttamente (apparentemente) in modalità Debug!
Un vero problema di graffio della testa che apprezzerebbe qualsiasi aiuto!
Per il supporto, questo era per lo più giusto. Dopo aver contattato il fornitore di terze parti, abbiamo scoperto che sono stati compilati utilizzando cdecl spec e non la stdcall necessaria per la conformità al codice gestito: http://msdn.microsoft.com/en-us/library/367eeye0%28VS.80%29.aspx . Ho aggiunto una domanda su StackOverflow per chiedere perché deve essere così? Si spera che qualcuno fornisca una spiegazione migliore rispetto all'articolo MSDN di riferimento. – TomO
Nelle impostazioni del progetto è presente un valore predefinito per la convenzione di chiamata utilizzata (C/C++) se non specificato con __declspec(). Questa convenzione di chiamata non era visibile nel codice. Ciò che accade con le convenzioni di mismatching è chiaro: se la responsabilità per la pulizia dello stack non è corrispondente, essa distrugge lo stack (non viene ripristinato allo stato precedente alla chiamata a causa di una doppia pulizia o troppo pochi). Questo dipende dalla quantità di argomenti passati nello stack. http://en.wikipedia.org/wiki/Calling_convention – jdehaan