Ich habe einen C ++ - Client für eine C ++ / CLI-DLL, die eine Reihe von C # -Dlls initialisiert.
Das hat früher funktioniert. Der Code, der fehlschlägt, hat sich nicht geändert. Der Code, der geändert wurde, wird nicht vor dem Auslösen der Ausnahme aufgerufen. Meine Kompilierungsumgebung hat sich geändert, aber das erneute Kompilieren auf einem Computer mit einer der alten ähnlichen Umgebung ist immer noch fehlgeschlagen. (BEARBEITEN: wie wir in der Antwort sehen, ist dies nicht ganz richtig, ich habe nur die Bibliothek in der alten Umgebung neu kompiliert, nicht die Bibliothek und den Client zusammen. Die Client-Projekte wurden aktualisiert und konnten nicht einfach zurückgehen.)
Jemand außer mir hat die Bibliothek neu kompiliert und wir haben Probleme mit der Speicherverwaltung bekommen. The pointer passed in as a String must not be in the bottom 64K of the process's address space.
Ich habe es neu kompiliert, und alles funktionierte gut ohne Codeänderungen. (Alarm # 1) Vor kurzem wurde es neu kompiliert, und Speicherverwaltungsprobleme mit Zeichenfolgen erschienen wieder, und dieses Mal gehen sie nicht weg. Der neue Fehler ist Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Ich bin mir ziemlich sicher, dass das Problem nicht dort liegt, wo ich die Ausnahme sehe, der Code hat sich zwischen den erfolgreichen und den fehlgeschlagenen Builds nicht geändert, aber wir sollten das überprüfen, um abgeschlossen zu sein. Ignoriere die Namen der Dinge, ich habe nicht viel Kontrolle über das Design dessen, was es mit diesen Strings macht. Und sorry für die Verwirrung, aber beachte, dass _bridge
und bridge
unterschiedliche Dinge sind. Viele Codezeilen fehlen, weil diese Frage schon zu lang ist.
In Bibliothek definiert:
%Vor%In der Client-Funktion:
%Vor%Beachten Sie, dass der Aufruf der Bibliothek, die abstürzt, im selben Bereich liegt wie die Vektordeklaration, die Strukturdeklaration, die Zeichenfolgenzuweisung und das Vektor-Pushback Es gibt keine Threading-Aufrufe in diesem Codeabschnitt, aber es werden andere Threads ausgeführt, die andere Dinge ausführen. Hier gibt es keine Zeigermathematik, es gibt keine Heapzuweisungen in dem Bereich außer vielleicht in der Standardbibliothek.
Ich kann den Code bis zum Aufruf Bridge_GetConfiguredDefaultsImplementationPointer
im Debugger ausführen und der Inhalt des Vektors configs
im Debugger korrekt aussehen.
Zurück in der Bibliothek, in der ersten Unterfunktion, wo der Debugger nicht scheint, habe ich die fehlerhafte Aussage in mehrere Konsolendrucke aufgeteilt.
%Vor% Ich bekomme die gleiche Ausnahme beim Zugriff von bee
, wenn ich die newConfigs[i].bee
über die Zuweisung von temp
verschiebe oder die Listendeklaration / -zuordnung auskommentiere.
Aber es war die Nichtübereinstimmung, nicht die spezifische Version, die das Problem war
Ja, das ist das Gesetz des schwarzen Buchstabens in VS. Sie haben leider nur die Gegenmaßnahmen vermisst, die in VS2012 eingebaut wurden, um diesen Fehler in einen diagnostizierbaren Linkerfehler umzuwandeln. Zuvor (und in VS2010) würde die CRT ihren eigenen Heap mit HeapAlloc () zuweisen. Jetzt (in VS2013) verwendet es den Standard-Prozess-Heap, der von GetProcessHeap () zurückgegeben wird.
Was an sich schon ausreicht, um einen AVE auszulösen, wenn Sie Ihre Anwendung unter Vista oder höher ausführen, wird Speicher von einem Heapspeicher zugewiesen und von einem anderen Heap freigegeben. Ein Debugger bricht zur Laufzeit beim Debuggen mit aktiviertem Debug Heap ab.
Dies ist nicht dort, wo es endet, ein anderes wichtiges Problem ist, dass das std :: string-Objekt-Layout nicht das gleiche zwischen den Versionen ist. Etwas, das Sie mit einem kleinen Testprogramm entdecken können:
%Vor% Ich habe eine vage Erinnerung an Stephen Lavavej, der die Reduktion der std :: string-Objektgröße erwähnt, die sehr viel als Feature präsentiert wird, aber ich kann sie nicht finden. Die zusätzlichen 4 Byte im Debug-Build werden durch die Iterator-Debugging-Funktion verursacht. Sie kann in den Präprozessor-Definitionen mit _HAS_ITERATOR_DEBUGGING=0
deaktiviert werden. Keine Funktion, die Sie schnell wegwerfen möchten, aber das Mischen von Debug- und Release-Builds der EXE und ihrer DLLs ist ziemlich tödlich.
Unnötig zu sagen, dass die verschiedenen Objektgrößen ernsthaft Bytes sind, wenn das Config-Objekt in einer DLL erstellt wird, die mit einer Version der C ++ - Standardbibliothek erstellt und in einer anderen verwendet wird. Viele Missgeschicke, die einfachste ist, dass der Code einfach das Config :: bee-Element vom falschen Offset liest. Ein AVE ist (fast) garantiert. Es gibt viel mehr Ärger, wenn Code den kleinen Geschmack des Config-Objekts zuweist, aber den großen Flavor von std :: string schreibt, der den Heap oder den Stack-Frame zufällig verfälscht.
Nicht mischen.
Ich glaube, dass 2013 viele Änderungen in den internen Datenformaten von STL-Containern eingeführt wurden, um die Speicherauslastung zu reduzieren und die Leistung zu verbessern. Ich weiß, vector
wurde kleiner, und string
ist im Grunde eine verherrlichte vector<char>
.
Microsoft erkennt die Inkompatibilität an:
"Um neue Optimierungen und Debugging-Prüfungen zu ermöglichen, Visual Studio Implementierung der C ++ - Standardbibliothek bricht absichtlich binär Kompatibilität von einer Version zur nächsten. Daher, wenn das C ++ Standard-Bibliothek verwendet wird, Objektdateien und statische Bibliotheken, die sind kompiliert mit verschiedenen Versionen können nicht in einer Binärdatei (EXE oder DLL), und C ++ - Standardbibliothek-Objekte können nicht zwischen übergeben werden Binärdateien, die mit verschiedenen Versionen kompiliert werden. "
Wenn Sie std::*
-Objekte zwischen ausführbaren Dateien und / oder DLLs übergeben, müssen Sie unbedingt sicherstellen, dass sie dieselbe Version des Compilers verwenden. Es wäre gut beraten, wenn Ihr Client und seine DLLs beim Start in irgendeiner Weise verhandeln und alle verfügbaren Versionen (z. B. Compiler-Version + Flags, Boost-Version, DirectX-Version usw.) vergleichen, damit Sie solche Fehler schnell finden. Stellen Sie es sich als eine modulübergreifende Behauptung vor.
Wenn Sie bestätigen möchten, dass dies das Problem ist, können Sie einige der Datenstrukturen, die Sie hin und her übergeben, auswählen und deren Größe im Client im Vergleich zu den DLLs überprüfen. Ich vermute, dass Ihre Config
-Klasse oben in einem der Fehlerfälle anders registriert wird.
Ich möchte auch erwähnen, dass es in erster Linie eine schlechte Idee ist, intelligente Container in DLL-Aufrufen zu verwenden. Sofern Sie nicht garantieren können, dass die App und die DLL nicht versuchen, die internen Puffer der anderen Container freizugeben oder neu zuzuweisen, könnten Sie leicht Probleme mit Heap-Problemen bekommen, da die App und DLL jeweils ihren eigenen internen C ++ - Heap haben. Ich denke, dass dieses Verhalten bestenfalls undefiniert ist. Auch das Übergeben von const&
Argumenten kann in seltenen Fällen immer noch zu einer Neuzuweisung führen, da const
einen Compiler nicht daran hindert, mit mutable
internals zu spielen.
Sie scheinen Speicherkorruption zu haben. Microsoft Application Verifier ist bei der Suche nach Korruption von unschätzbarem Wert. Benutze es, um deinen Fehler zu finden:
Basics\Heaps
. PS: Es ist eine gute Idee, den Application Verifier jederzeit für Ihr Entwicklungsprojekt zu aktivieren.
Tags und Links .net c++ access-violation memory-management c++-cli