So habe ich eine native 3rd-Party-C++ - Code-Basis Ich arbeite mit .lib und .hpp-Dateien, die ich verwendet, um einen Wrapper in C++/CLI für die eventuelle Verwendung zu erstellen in C#.Zugriffsverletzung Ausnahme/Absturz von C++ - Callback zu C# -Funktion
Ich habe ein bestimmtes Problem beim Wechsel vom Debug- zum Freigabemodus festgestellt, in dem ich eine Zugriffsverletzung-Ausnahme erhalte, wenn der Code eines Rückrufs zurückkehrt.
der Code aus den ursprünglichen HPP-Dateien für Callback-Funktion Format:
typedef int (*CallbackFunction) (void *inst, const void *data);
-Code aus dem C++/CLI Wrapper für Callback-Funktion Format: (ich erklären würde, warum ich zwei in einem Moment erklärt)
public delegate int ManagedCallbackFunction (IntPtr oInst, const IntPtr oData);
public delegate int UnManagedCallbackFunction (void* inst, const void* data);
--Quickly, der Grund, erklärte ich eine zweite „UnManagedCallbackFunction“ ist, dass ich versuchte, einen „Vermittler“ Rückruf in der Hülle zu schaffen, so dass die Kette geändert von native C++> C# zu einer Version von native C++> C++/CLI Wrapper> C# ... Vollständige Beschreibung ure, das Problem lebt immer noch, es wurde gerade auf die gleiche Zeile in den C++/CLI Wrapper geschoben (die Rückkehr).
Und schließlich, das Krachen Code von 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;
}
Alle schreiben an die Konsole sind fertig und dann sehen wir den gefürchteten Absturz auf der Rückkehr:
Unbehandelte Ausnahme bei 0x04d1004c in helloworld.exe: 0xC0000005: Zugriff Verletzung Leseort 0x04d1004c.
Wenn ich in die Debugger Schritt von hier, alles was ich sehe ist, dass der letzte Eintrag auf dem Call-Stack ist:> „04d1004c()“, die von einem Dezimalwert auswertet: 80805964
Welche ist nur dann interessant, wenn Sie an der Konsole sehen, die zeigt:
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
Nun, ich weiß, dass zwischen Debug und Release einige Dinge in der Microsoft-Welt ganz anders sind. Ich bin natürlich besorgt über Byte-Padding und die Initialisierung von Variablen, wenn also etwas hier nicht zur Verfügung steht, lassen Sie es mich wissen und ich werde den (schon langen) Beitrag hinzufügen. Ich denke auch, dass der verwaltete Code NICHT alle Eigentumsrechte freigibt und dann versucht das native C++ - Zeug (für das ich nicht den Code habe) das pData-Objekt zu löschen oder zu töten, wodurch die App zum Absturz gebracht wird.
Weitere vollständige Offenlegung, es funktioniert alles (scheinbar) im Debug-Modus!
Ein echtes Kopf-Scratch-Problem, das jede Hilfe zu schätzen wissen würde!
Für die Unterstützung war dies meistens richtig. Nach der Kontaktaufnahme mit dem Drittanbieter haben wir festgestellt, dass sie mit cdecl spec und nicht mit dem für die Einhaltung von Managed Code erforderlichen stdcall kompiliert wurden: http://msdn.microsoft.com/en-us/library/367eey0%28VS.80%29.aspx . Ich habe eine Frage zu StackOverflow gestellt, um zu fragen, warum das so sein muss? Hoffentlich gibt jemand eine bessere Erklärung als der referenzierte MSDN-Artikel. – TomO
In den Projekteinstellungen gibt es einen Standard für die verwendete Aufrufkonvention (C/C++), falls nicht mit __declspec() angegeben. Diese Aufrufkonvention war im Code nicht sichtbar. Was bei nicht übereinstimmenden Konventionen passiert, ist klar: Wenn die Verantwortung für die Stapelbereinigung nicht zusammenpasst, wird der Stapel zerquetscht (vor dem Aufruf wird er nicht auf seinen Status zurückgesetzt, da entweder doppelt aufgeräumt oder zu wenige vorhanden sind). Dies hängt von der Anzahl der Argumente ab, die auf dem Stack übergeben werden. http://en.wikipedia.org/wiki/Calling_convention – jdehaan