Von Anfang an: Seit dem 1. März 2017 ist dies ein Bug, der von Microsoft bestätigt wurde. Lesen Sie die Kommentare am Ende.
Kurzbeschreibung:
Ich habe zufällige Abstürze in größeren Anwendungen mit MFC, ATL. In all diesen Fällen, nachdem ATL-Subklassen für ein Fenster bei einfachen Aktionen mit einem Fenster verwendet wurden (Verschieben, Größenänderung, Fokus setzen, Malen usw.), bekomme ich einen Absturz bei einer zufälligen Ausführungsadresse.
Zuerst sah es aus wie eine wilde Pointer- oder Heap-Beschädigung, aber ich beschränkte das komplette Szenario auf eine sehr einfache Anwendung, die reines ATL und nur Windows API verwendete.
Anforderungen / meine verwendeten Szenarien:
Was die Anwendung macht:
Er erstellt einfach ein Rahmenfenster und versucht, viele statische Fenster mit der Windows-API zu erstellen. Nachdem das statische Fenster erstellt wurde, unterliegt dieses Fenster der ATL CWindowImpl :: SubclassWindow-Methode. Nach der Unterklassenoperation wird eine einfache Fensternachricht gesendet.
Was passiert:
Nicht bei jedem Lauf, aber sehr oft stürzt die Anwendung bei SendMessage auf das Unterklasse-Fenster ab. Auf dem 257-Fenster (oder einem anderen Vielfachen von 256 + 1) schlägt die Unterklasse in irgendeiner Weise fehl. Der ATL-Thunk, der erstellt wird, ist ungültig. Es scheint, dass die gespeicherte Ausführungsadresse der neuen Unterklassenfunktion nicht korrekt ist. Das Senden der Nachricht an das Fenster verursacht einen Absturz. Der Callstack ist immer derselbe. Die letzte sichtbare und bekannte Adresse im Callstack befindet sich in der atlthunk.dll
%Vor%Die geworfene Ausnahme im Debugger wird angezeigt als:
%Vor%oder ein anderes Beispiel
%Vor%Was ich über atlthunk.dll weiß:
Atlthunk.dll scheint nur Teil des 64-Bit-Betriebssystems zu sein. Ich habe es auf einem Win 8.1 und Win 10 Systemen gefunden.
Wenn atlthunk.dll verfügbar ist (alle Windows 10-Rechner), kümmert sich diese DLL um das Thunking. Wenn die DLL nicht vorhanden ist, wird das Thunking auf die übliche Weise ausgeführt: Zuweisen eines Blocks zum Heap, Markieren als ausführbar, Hinzufügen von Lade- und Sprunganweisungen.
Wenn die DLL vorhanden ist. Es enthält 256 vordefinierte Slots für Unterklassen. Wenn 256 Unterklassen fertig sind, lädt sich die DLL ein zweites Mal in den Speicher und verwendet die nächsten 256 verfügbaren Steckplätze in der DLL.
Soweit ich sehe, gehört die atlthunk.dll zu Windows 10 und ist nicht austauschbar oder verteilbar.
Geprüfte Dinge:
Reproduzierbarkeit:
Das Problem ist irgendwie reproduzierbar. Es stürzt nicht ständig ab, es stürzt wahllos ab. Ich habe eine Maschine, bei der der Code bei jeder dritten Ausführung abstürzt.
Ich kann es auf zwei Desktop-Stationen mit i7-4770 und einem i7-6700 abbilden.
Andere Maschinen scheinen überhaupt nicht betroffen zu sein (funktioniert immer auf einem Laptop i3-3217 oder Desktop mit i7-870)
Über das Beispiel:
Der Einfachheit halber verwende ich einen SEH-Handler, um den Fehler zu finden. Wenn Sie die Anwendung debuggen, zeigt der Debugger den oben erwähnten Callstack an. Das Programm kann mit einer Ganzzahl in der Befehlszeile gestartet werden. In diesem Fall startet das Programm erneut mit der Zählung um 1.So dekrementiert, wenn Sie CrashAtlThunk 100 starten, wird die Anwendung 100 Mal gestartet. Bei einem Fehler fängt der SEH-Handler den Fehler auf und zeigt den Text "Crash" in einem Meldungsfeld an. Wenn die Anwendung ohne Fehler ausgeführt wird, zeigt die Anwendung in einem Meldungsfeld "Succeeded" an. Wenn die Anwendung ohne Parameter gestartet wird, wird sie nur einmal ausgeführt.
Fragen:
Hinweise:
2017-01-20 Support-Fall bei Microsoft geöffnet.
Der Code
%Vor%Kommentar nach der Antwort von Eugene (24. Februar 2017):
Ich möchte meine ursprüngliche Frage nicht ändern, aber ich möchte einige zusätzliche Informationen hinzufügen, wie man dies in eine 100% Repro bringt.
1, Ändern Sie die Hauptfunktion zu
%Vor%Heben Sie die Entgegennahme des LoadLibrary-Aufrufs auf. Kompilieren.
Führen Sie das Programm einmal aus und stoppen Sie es im Debugger. Notieren Sie die Adresse, unter der die Bibliothek geladen wurde (hMod).
Stoppen Sie das Programm. Kommentieren Sie nun den Library-Aufruf erneut und ändern Sie den VirtualAlloc
-Aufruf auf die Adresse des vorherigen hMod-Wertes, dies ist die bevorzugte Ladeadresse in dieser Fenstersitzung.
Kompilieren Sie erneut und führen Sie es aus. CRASH!
Danke an eugene.
Bis jetzt. Microsoft untersucht das immer noch. Sie haben Dumps und alles Code. Aber ich habe keine endgültige Antwort. Tatsache ist, dass wir in einigen Windows 64bit OS einen fatalen Fehler haben .
Ich habe die folgenden Änderungen vorgenommen, um dies zu umgehen
Öffnen Sie atlstdthunk.h von VS-2015.
Entfernen Sie den Block #ifdef vollständig, der USE_ATL_THUNK2 definiert. Codezeilen 25 bis 27.
Kompilieren Sie Ihr Programm erneut.
Dies ermöglicht den alten Thunking-Mechanismus, der von VC-2010, VC-2013 ... bekannt ist, und dies funktioniert für mich ohne Absturz. Solange keine anderen bereits kompilierten Bibliotheken beteiligt sind, die 256 Fenster über ATL in irgendeiner Weise ableiten oder verwenden können.
Kommentar (1. März 2017):
Tatsächlich sagt das. Solange es keinen stabilen Patch gibt, kann ich nie wieder den normalen ATL-Thunking verwenden, weil ich nie wissen werde, welche Windows-Versionen auf der Welt mein Programm benutzen werden. Weil Windows 8 und Windows 8.1 und Windows 10 vor RS2 unter diesem Fehler leiden werden.
Letzter Kommentar (9. März 2017):
Mein Rat für alle Programmierer: Ändere das atlstdthunk.h in deiner Visual Studio Version VS-2015, VS-2017 (siehe oben). tue ich nicht verstehe Microsoft. Dieser Fehler ist ein schwerwiegendes Problem beim ATL Thunking. Es kann jeden Programmierer treffen, der eine größere Anzahl von Fenstern und / oder Unterklassen verwendet.
Wir kennen nur eine Korrektur in Windows 10 RS2. Also alle älteren OS sind betroffen! Daher empfehle ich, die Verwendung der atlthunk.dll durch Auskommentieren der oben genannten Definition zu deaktivieren.
Dies ist der Fehler in atlthunk.dll. Wenn es zum zweiten Mal und später geladen wird, geschieht dies manuell über den MapViewOfFile-Aufruf. In diesem Fall wird nicht jede Adresse in Bezug auf die Modulbasis richtig geändert (wenn DLL geladen durch LoadLibarary / LoadLibraryEx Aufrufe System Loader tut dies automatisch). Wenn dann die erste Zeit-DLL auf bevorzugte Basisadresse geladen wurde, funktioniert alles einwandfrei, da unveränderte Adressen auf den ähnlichen Code oder die gleichen Daten zeigen. Aber wenn nicht, stürzt man ab, wenn das 257. Unterklasse-Fenster Nachrichten verarbeitet.
Seit Vista haben wir die Funktion "Adressraum-Layout-Randomisierung", die erklärt, warum Ihr Code zufällig abstürzt. Absturz jedes Mal, wenn Sie atlthunk.dll Basisadresse auf Ihrem Betriebssystem (es unterscheidet sich bei verschiedenen Betriebssystemversionen) finden und eine Speicherseite Adressraum Reservierung an dieser Adresse mit VirtualAlloc Aufruf vor der ersten Unterklasse . Um die Basisadresse zu finden, können Sie den Befehl dumpbin /headers atlthunk.dll
verwenden oder PE-Header manuell analysieren.
Mein Test zeigt, dass unter Windows 10 die Version 14393.693 x32 betroffen ist, aber x64 nicht. Auf dem Server 2012R2 mit den neuesten Updates sind beide (x32 und x64) Versionen betroffen.
BTW, atlthunk.dll-Code hat etwa 10 mal mehr CPU-Anweisungen pro Thunk-Aufruf als vorherige Implementierung. Es kann nicht sehr wichtig sein, aber es verlangsamt die Verarbeitung der Nachricht.
Tags und Links c++ windows-10 visual-studio-2017 visual-studio-2015 atl