Was „Keine Rückmeldung“ wirklich ist — Wie Windows entscheidet, dass eine App hängt, und Entwürfe, die nicht hängen

· Aktualisiert am: · · Windows, Windows-Entwicklung, Fehleruntersuchung, Multithreading, WinForms, WPF, Win32-API, UI-Design

Änderungsverlauf (Erstfassung, veröffentlicht am 22. Aug 2026)
Erstveröffentlichung
Diesen Artikel zitieren(DOI (registriertes Archiv): 10.5281/zenodo.22176661)

Die folgenden DOIs verweisen auf bereits archivierte Versionen, die vom aktuellen Text abweichen können. Verwenden Sie die URL dieser Seite, um auf den aktuellen Text zu verweisen.

Go Komura (2026). Was „Keine Rückmeldung“ wirklich ist — Wie Windows entscheidet, dass eine App hängt, und Entwürfe, die nicht hängen. KomuraSoft LLC. https://comcomponent.com/de/blog/windows-app-not-responding-hang-mechanism/

DOI (registriertes Archiv)
10.5281/zenodo.22176661
DOI (zuletzt registrierte Version)
10.5281/zenodo.22176662

„Während der Verarbeitung wird der Bildschirm weiß, und in der Titelleiste steht ‚Keine Rückmeldung‘.“ „Die Benutzer sagen, es hänge gelegentlich, aber auf der Entwicklungsmaschine reproduziert es sich nie.“ Das sind häufige Beschwerden über Windows-Geschäftsanwendungen.

Als Erstes gilt: Nicht die App selbst zeigt „Keine Rückmeldung“ an, sondern Windows. Windows erkennt, dass die Nachrichtenverarbeitung des Fensters stehen geblieben ist, und tauscht den ursprünglichen Bildschirm gegen ein Ersatzfenster. Der Schlüssel zur Ursache ist, was der UI-Thread, dem dieses Fenster gehört, tut, sodass er die nächste Nachricht nicht verarbeiten kann.12

Dieser Artikel richtet sich an Entwickler, die Geschäftsanwendungen unter Windows bauen, und an IT-Verantwortliche, die Anfragen zu hängenden Apps entgegennehmen. Er geht in der Reihenfolge Prüfung durch das Betriebssystem → Nachrichtenschleife → Eingrenzung der Ursache nach Kategorie → Entwürfe, die nicht hängen → Untersuchungsverfahren vor. Wenn Sie eine gerade hängende App untersuchen wollen, lesen Sie zuerst Kapitel 7.

1. Zuerst das Fazit: Die Anzeige nicht ausblenden, sondern den UI-Thread freihalten

Mechanismus, Reparatur und Untersuchung von „Keine Rückmeldung“ werden übersichtlicher, wenn man sie so aufteilt.

Was Sie wissen oder lösen wollen Das Erste, das festzuhalten ist Ausführliche Erklärung
Wonach schaut Windows, um ohne Rückmeldung zu urteilen? Es schaut auf das Fenster und die Nachrichtenverarbeitung des GUI-Threads, dem es gehört. Die Prüfung gilt nicht für den Prozess als Ganzes Kapitel 2
Warum bleiben Schaltflächen und Neuzeichnen stehen? Der UI-Thread kommt aus einer Ereignisbehandlung oder einem Warten nicht zurück und kann die nächste Nachricht nicht abholen Kapitel 3 und 4
Bei langwieriger Arbeit soll der Bildschirm nicht einfrieren CPU-Arbeit und APIs, die nur synchron existieren, auf einen Worker auslagern; für I/O asynchrone APIs nutzen Kapitel 5
Darf man die Anzeige nur mit DoEvents oder einer Einstellung umgehen? Das lädt Wiedereintrittsfehler ein oder blendet die Anzeige nur aus. Keine Ursachenlösung Kapitel 6
Die Ursache gelegentlicher Hänger finden Den Dump im hängenden Moment nehmen, bevor Sie beenden oder neu starten Kapitel 7

Das Prinzip der Gegenmaßnahme ist: Auf dem UI-Thread nicht lange warten und nicht schwer rechnen. Der UI-Thread übernimmt Eingabe, Zeichnen, Fortschrittsanzeige und das Annehmen von Abbruch und wird von zeitaufwendiger Arbeit getrennt.3

Außerdem sind die Zeit bis zur Anzeige „Keine Rückmeldung“ und die Antwortzeit, die sich noch angenehm bedienen lässt, zwei verschiedene Dinge. Unter 5 Sekunden zu bleiben reicht nicht. Abschnitt 4.5 erklärt diesen Unterschied.

2. „Keine Rückmeldung“ ist das Urteil des Betriebssystems: die 5-Sekunden-Regel und der Ersatzbildschirm

2.1 Die Einheit der Beurteilung ist nicht der ganze Prozess, sondern das Fenster und sein besitzender Thread

In Microsofts Beschreibung von IsHungAppWindow gilt ein Fenster als ohne Rückmeldung, wenn es die folgenden Bedingungen erfüllt.1

Bedingung Inhalt
Wartet nicht auf Eingabe Es befindet sich nicht im Zustand des Wartens auf Eingabe
Nicht in der Startverarbeitung Die App ist nicht in ihrer Startverarbeitung
Holt keine Nachrichten ab Es hat PeekMessage für das interne Timeout von 5 Sekunden nicht aufgerufen

Das Betriebssystem schaut nicht darauf, was die App berechnet, sondern darauf, ob die Nachrichtenschleife läuft. Die Nachrichtenschleife ist der Mechanismus, der Eingabe- und Neuzeichenanforderungen der Reihe nach abholt und verarbeitet. Kapitel 3 erklärt, wie das konkret abläuft.

Dieselbe Dokumentation hält auch fest, dass der Wert von 5 Sekunden sich in Zukunft ändern kann. Es ist das interne Timeout der Prüfung ohne Rückmeldung, kein Entwurfsmaßstab, der sagt „die UI darf bis zu 5 Sekunden stehen bleiben“.1

Die Prüfung gilt nicht pro Prozess. In einer App mit mehreren UI-Threads kann ein Fenster hängen, während Fenster, die anderen Threads gehören, weiterlaufen. Auch in der Untersuchung müssen Sie über den Prozess hinausgehen und den Thread identifizieren, dem das hängende Fenster gehört.

2.2 Der milchig-weiße Bildschirm ist ein „Geisterfenster“

Wenn ein Fenster der obersten Ebene als ohne Rückmeldung beurteilt wird, blendet Windows das ursprüngliche Fenster aus und tauscht es gegen ein Geisterfenster mit derselben Z-Reihenfolge, Position, Größe und Erscheinung. Das „(Keine Rückmeldung)“ im Titel und das milchig-weiße Aussehen unter dem Aero-Design kommen von diesem Ersatzbildschirm, nicht von der hängenden App.2

Zustand des Bildschirms Was der Benutzer sieht
Das ursprüngliche Fenster der App Kann Nachrichten nicht verarbeiten; reagiert nicht auf Klicks oder Neuzeichnen
Das Geisterfenster, das das Betriebssystem bereitstellt Begrenzte Operationen wie Verschieben, Größenänderung, Minimieren und Schließen

Dass sich das Ersatzfenster bewegen lässt, bedeutet nicht, dass der Inhalt der App wieder arbeitet. Das Betriebssystem übernimmt nur die Mindestoperationen.24

Auch eine Beschwerde, „die Anzeige kommt zu früh“, fassen Sie zuerst als Problem auf, dass der UI-Thread lange nicht zur Nachrichtenverarbeitung zurückkehrt. Die stehende Verarbeitung zu untersuchen kommt vor dem Unterdrücken der Anzeige.

2.3 Dass es unter dem Debugger nicht erscheint, heißt nicht, dass es nicht hängt

Während ein Debugger verbunden ist, legt das Betriebssystem keine Geisterfenster an. Deshalb kann ein Unterschied wie „unter dem Debugger erscheint es nicht, in der normalen Ausführung geht es auf Keine Rückmeldung“ trotzdem bedeuten, dass der UI-Thread genauso blockiert ist und sich nur die Anzeige unterscheidet.2

Es gibt auch eine API, die den Tausch gegen ein Geisterfenster pro Prozess deaktiviert, aber sie ist keine API, die den Hänger selbst behebt. Abschnitt 6.2 trennt den Verwendungszweck.

3. Warum der Bildschirm stehen bleibt: UI-Thread und Nachrichtenschleife

3.1 Eingabe und Neuzeichnen verarbeitet derselbe Thread

Windows-GUI-Apps sind ereignisgetrieben. Sie empfangen Nachrichten für Maus, Tastatur, Neuzeichenanforderungen, Timer und so weiter und führen die jeweils zugehörige Verarbeitung aus. Jeder Thread, der ein Fenster anlegt, hat eine Nachrichtenwarteschlange und führt eine Schleife wie die folgende aus.5

MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);   // die Fensterprozedur wird aufgerufen
}

GetMessage holt eine Nachricht aus der Warteschlange, und DispatchMessage ruft die Fensterprozedur des Fensters auf. Die Fensterprozedur ist die Funktion, die die Verarbeitung zu einer Nachricht ausführt. Die Behandlung eines Schaltflächenklicks, das Neuzeichnen und WinForms- und WPF-Ereignishandler hängen alle an dieser Nachrichtenverarbeitung auf dem UI-Thread.6

Grundstruktur der NachrichtenschleifeDas Betriebssystem legt Maus, Tastatur und andere Eingabe in die Nachrichtenwarteschlange des Threads; die Schleife des UI-Threads holt sie mit GetMessage ab, ruft die Fensterprozedur mit DispatchMessage auf und kehrt zum Anfang der Schleife zurück, wenn die Verarbeitung fertig istBetriebssystem (Eingabe, Neuzeichenanforderungen, Timer)Nachrichtenwarteschlange des ThreadsMit GetMessage abholenDispatchMessageIn der Fensterprozedur verarbeiten

Abbildung 1: Eine Nachricht abholen, in der Fensterprozedur verarbeiten und zur Schleife zurückkehren. Dieser Umlauf trägt Eingabe und Zeichnen.

Was geschieht nun, wenn aus einem Klickhandler eine lange Verarbeitung aufgerufen wird? Bis diese Verarbeitung zurückkehrt, kann der UI-Thread die nächste Nachricht nicht abholen. Klicks und Neuzeichenanforderungen, die danach eintreffen, lassen sich nicht mehr verarbeiten. Das ist die Grundstruktur eines „eingefrorenen Bildschirms“.

3.2 PostMessage und SendMessage kehren zu unterschiedlichen Zeitpunkten zurück

Die Zustellung von Nachrichten hat zwei Wege: einen, der die Nachricht auf die Warteschlange legt, und einen, der auf den Abschluss der Verarbeitung wartet.57

API Verhalten Wann der Aufrufer zurückkehrt
PostMessage Legt die Nachricht auf die Warteschlange; die Empfängerseite holt sie ab und verarbeitet sie Kehrt zurück, sobald die Nachricht eingereiht ist. Wartet nicht auf den Abschluss der Verarbeitung
SendMessage Sendet die Nachricht an die Fensterprozedur und lässt sie ausführen Kehrt nicht zurück, bis die Fensterprozedur fertig ist

Besonders wenn SendMessage an ein Fenster auf einem anderen Thread gesendet wird, muss auch die Senderseite warten, bis die Empfängerseite die Nachricht verarbeiten kann. Dieser Unterschied führt direkt in den Deadlock von Abschnitt 4.3.7

4. Ursachen nach Kategorie: fünf Muster, die den UI-Thread blockieren

Das Aussehen von „Keine Rückmeldung“ ist dasselbe, aber was blockiert, unterscheidet sich. Trennen Sie zuerst die Kandidaten und bestätigen Sie sie schließlich mit den Dumps und Aufzeichnungen aus Kapitel 7.

Kandidatenursache Was auf dem UI-Thread geschieht Typisches Erscheinungsbild
Synchrones I/O oder Netzaufruf Wartet auf den Abschluss von Datei, Datenbank, Web-API oder Ähnlichem Auf der Entwicklungsmaschine schnell, hängt aber in der Produktion oder in bestimmten Umgebungen
Sperrwarten Kann eine vom Worker gehaltene Sperre nicht erwerben und wartet Hängt selten, je nachdem, wie sich Verarbeitungen überlappen
SendMessage zwischen Threads Wartet darauf, dass ein anderer Thread die Nachricht verarbeitet Gerät in gegenseitiges Warten oder in einen Broadcast
Aufruf in ein COM-STA Wartet auf die Nachrichtenverarbeitung des Ziel-STA Ein stehender UI-Thread greift auf COM-Aufrufe anderer Threads über
Aneinanderreihung kurzer Verarbeitungen Jede einzelne ist kurz, aber die Hintereinanderausführung kehrt nicht zur Schleife zurück Die Bedienung wird nur träge, wenn die Stückzahl wächst

4.1 Synchrones I/O und Netzaufrufe

Das häufigste Muster ist, im Klickhandler einer Schaltfläche das Lesen oder Schreiben einer großen Datei, eine Datenbankabfrage, eine Web-API oder den Zugriff auf ein Netzlaufwerk synchron auszuführen.

Was auf der Entwicklungsmaschine in einem Bruchteil einer Sekunde fertig ist, kann unter Produktionslatenz oder bei einem Problem des Dateiservers zu einem Warten von mehreren zehn Sekunden werden. Netzlaufwerke haben bei Verbindungsabbruch lange Timeouts, was das Symptom verschlimmert. „Auf der Entwicklungsmaschine war es schnell“ ist kein Grund, auf dem UI-Thread zu warten.

4.2 Der UI-Thread wartet auf eine Sperre, die ein Worker hält

Sperren, die gemeinsame Daten schützen, können die UI ebenfalls anhalten. Wenn ein Worker eine Sperre lange hält und der UI-Thread dieselbe Sperre nehmen will, wartet der UI-Thread, bis er sie erwerben kann.

Auch wenn Sie die Arbeit auf einen Worker verlagern: Der Bildschirm wird nicht frei, solange der UI-Thread auf das Ende dieser Arbeit oder auf die Freigabe der Sperre wartet. Die Disziplin von Sperren behandeln wir ausführlich in der praktischen Multithreading-Serie.

4.3 Über SendMessage auf den Abschluss des anderen warten

Der klassische Deadlock ist die Kombination, in der der UI-Thread auf das Ende eines Workers wartet und dieser Worker SendMessage an die UI sendet und wartet. Die UI ist im Warten und kann keine Nachrichten verarbeiten, und der Worker kommt aus SendMessage nicht zurück, sodass keiner von beiden vorankommt.75

Deadlock durch SendMessage zwischen ThreadsWenn ein Worker-Thread SendMessage an das Fenster des UI-Threads sendet, während der UI-Thread blockiert auf das Ergebnis des Workers wartet, warten beide auf den Abschluss des anderen und es entsteht ein DeadlockWorker-ThreadUI-ThreadWorker-ThreadUI-ThreadKann Nachrichten nicht verarbeiten (wartet)Kann aus SendMessage nicht zurückkehrenGegenseitiges Warten: DeadlockAuf den Abschluss des Workers warten (blockiert)SendMessage (kehrt erst nach der Verarbeitung zurück)

Abbildung 2: Die Auslagerung auf einen Worker allein reicht nicht. Wenn UI und Worker auf den Abschluss des anderen warten, entsteht ein Deadlock.

Auch das Senden an HWND_BROADCAST braucht Vorsicht. Ein einziges nicht antwortendes Fenster reicht, um die Senderseite mitzureißen. Wo Sie nicht weiterwarten können, ziehen Sie SendMessageTimeout mit Timeout oder PostMessage, das nicht auf den Abschluss wartet, in Betracht.8

4.4 Ein COM-STA bleibt stehen und reißt andere Threads mit

Aufrufe von anderen Threads in ein STA-Objekt werden als Fensternachrichten zugestellt. Deshalb blockieren COM-Aufrufe dorthin, wenn der UI-Thread (ein STA) blockiert ist. Es ist eine Struktur, in der nicht nur die UI, sondern auch die Aufruferseite in den Wartezustand gerät.

Den Zusammenhang von Apartments und Nachrichtenverarbeitung erklärt der Artikel zu COM STA/MTA.

4.5 „Einmal ist es ein Augenblick“ hundertmal hintereinander

Eine synchrone Verarbeitung von 50 ms addiert sich bei 100 Wiederholungen auf 5 Sekunden. Beurteilen Sie nicht, indem Sie einzelne Funktionen messen und sie als kurz einstufen; denken Sie in der Gesamtzeit, bis der UI-Thread zur Nachrichtenschleife zurückkehrt.

Spürbare Trägheit beginnt bei etwa 100 ms. Nehmen Sie nicht die 5 Sekunden der Prüfung ohne Rückmeldung als Ziel; entwerfen Sie so, dass der UI-Thread nur im Millisekundenbereich blockiert sein darf.

5. Entwürfe, die nicht hängen: Ausführung der Arbeit und Aktualisierung des Bildschirms trennen

5.1 Berechnung und synchrone APIs von asynchronem I/O trennen

Das Prinzip der Gegenmaßnahme ist, zeitaufwendige Arbeit vom UI-Thread herunterzunehmen. Nicht alles wird jedoch auf dieselbe Weise verlagert.

Art der Arbeit Grundlegende Behandlung in C# Rolle des UI-Threads
CPU-lastige Berechnung Mit Task.Run auf einen Worker-Thread verlagern Den Abschluss asynchron abwarten und das Ergebnis anzeigen
Verarbeitung, für die nur eine synchrone API existiert Auf einen Worker-Thread auslagern Die UI nicht durch synchrones Warten auf den Abschluss blockieren
I/O mit asynchroner API GetStringAsync oder Ähnliches mit await Während des I/O-Wartens zur Nachrichtenverarbeitung zurückkehren
Eingabe, Zeichnen, Fortschritt, Abbruch Auf dem UI-Thread übernehmen Keine lange Berechnung und kein synchrones I/O untermischen

Bei asynchronem I/O muss kein Thread nur zum Warten belegt werden. Auch das Codebeispiel unten trennt: Berechnung über Task.Run, HTTP-Kommunikation über die native asynchrone API.3

5.2 In C# mit async/await zur UI zurückkehren und aktualisieren

Das nächste Beispiel trennt in einem WinForms-Klickhandler schwere Arbeit und UI-Aktualisierung. Weil dieses Beispiel von einem UI-Ereignishandler startet und den Ausführungskontext der UI behält, kehrt die Fortsetzung nach await auf den UI-Thread zurück und kann Steuerelemente aktualisieren.3

private async void RunButton_Click(object sender, EventArgs e)
{
    runButton.Enabled = false;
    try
    {
        // CPU-lastige Verarbeitung und APIs, die nur synchron existieren, per Task.Run auf den Worker
        var result = await Task.Run(() => HeavyCalculation(input));

        // Für I/O eine nativ asynchrone API nutzen (sie verbraucht auch keinen Thread)
        var data = await httpClient.GetStringAsync(url);

        // Nach await sind wir auf dem UI-Thread, Steuerelemente dürfen direkt angefasst werden
        resultLabel.Text = result + data.Length.ToString();
    }
    catch (Exception ex)
    {
        // Eine Ausnahme, die aus einem async-void-Handler entweicht, stürzt die App ab. Hier abfangen
        MessageBox.Show($"Die Verarbeitung ist fehlgeschlagen: {ex.Message}");
    }
    finally
    {
        runButton.Enabled = true;
    }
}

Vier Punkte sind festzuhalten.

Stelle Absicht
Den Abschluss mit await abwarten Den UI-Thread bei einem Warten zur Nachrichtenschleife zurückgeben
Den Bildschirm nach await aktualisieren Steuerelemente nicht vom Worker aus anfassen
Die Schaltfläche während der Ausführung deaktivieren Verhindert, dass dieselbe Verarbeitung wiederholt gestartet wird
catch und finally Lässt Ausnahmen nicht aus dem async void-Handler entweichen und stellt den Schaltflächenzustand wieder her

Das ist ein Codebeispiel für die Rollenteilung; die Umsetzung von HeavyCalculation und Ähnlichem, der Umgang mit dem Schließen des Formulars sowie Fortschritt und Abbruch sind weggelassen. Fortschritt und Unterbrechung, die langwierige Arbeit braucht, bauen Sie gesondert ein, wie in Abschnitt 5.4.

WinForms-Steuerelemente und WPF-Elemente behandelt man von dem Thread aus, der sie angelegt hat. Sie direkt vom Worker anzufassen führt zu Ausnahmen oder undefiniertem Verhalten. Um ausdrücklich auf den UI-Thread zurückzukehren, nutzen Sie Control.Invoke / BeginInvoke in WinForms und Dispatcher.InvokeAsync in WPF.3

5.3 In Win32 die Fertigstellung mit PostMessage mitteilen

Die Rollenteilung ist in nativem Win32 dieselbe. Übergeben Sie die Arbeit an einen Worker, und wenn sie fertig ist, senden Sie mit PostMessage eine eigene Fertigstellungsnachricht und aktualisieren den Bildschirm in der Fensterprozedur des UI-Threads. Weil PostMessage zurückkehrt, sobald die Nachricht eingereiht ist, wartet der Worker nicht auf den Abschluss der UI-Verarbeitung.7

Rollenteilung einer App, die nicht hängtDer UI-Thread übernimmt nur Eingabe, Fortschrittsanzeige und das Annehmen von Abbruch; ein Worker-Thread führt die schwere Verarbeitung aus und gibt die Fertigstellung über PostMessage oder eine await-Fortsetzung an den UI-Thread zurückArbeit übergebenPostMessage / await-FortsetzungUI-Thread: Eingabe, Fortschritt, AbbruchWorker-Thread: schwere VerarbeitungKein synchrones I/O und keine lange Berechnung auf dem UI-Thread

Abbildung 3: Schwere Berechnung und synchrone Verarbeitung an den Worker übergeben und Bildschirmaktualisierungen auf den UI-Thread zurückholen. Asynchrones I/O wartet man wie in Abschnitt 5.1 mit einer asynchronen API.

Das Warten auf den Worker selbst schreiben Sie nach der Disziplin, die der Artikel zu Bedingungsvariablen behandelt. Wichtig ist auch, dass der UI-Thread den Worker nicht wieder synchron abwartet.

5.4 Fortschritt und Abbruch von Anfang an entwerfen

Auch wenn der Bildschirm nicht einfriert: Wenn sich lange nichts ändert, kann der Benutzer nicht erkennen, ob noch etwas läuft. Deshalb entwerfen Sie Fortschrittsanzeige und Abbruchannahme als Teil der Verarbeitung.

Richtung der Mitteilung Mechanismus Rolle
Worker → UI IProgress<T> Den Fortschritt an den Bildschirm melden
UI → Worker CancellationToken Die Aufforderung zum Anhalten übermitteln

Der Worker bricht an einer sinnvollen Stelle ab und räumt auf. Mittel für Fortschritt und Abbruch verringern die Situationen, in denen Benutzer zum erzwungenen Beenden greifen. Erzwungenes Beenden kann zu Datenbeschädigung führen.

6. Zwei Wege, die wie Auswege aussehen, und ihre Grenzen

6.1 DoEvents holt andere Ereignisse mitten in die Verarbeitung

Wenn Sie während schwerer Arbeit Application.DoEvents() oder eine PeekMessage-Schleife einschieben, werden Nachrichten verarbeitet und die Anzeige „Keine Rückmeldung“ vermieden. Allerdings läuft dann ein anderer Ereignishandler, während die ursprüngliche Verarbeitung noch nicht fertig ist. Das ist Wiedereintritt.

Zum Beispiel wird DoEvents mitten in der Datenverarbeitung aufgerufen, und ein aufgestauter zweiter Klick auf die Schaltfläche wird verarbeitet. Dieser Handler schreibt dieselben Daten um, und danach setzt die ursprüngliche Verarbeitung fort. In dieser Reihenfolge ist der Zustand, den die ursprüngliche Verarbeitung angenommen hat, bereits verloren.

Es treten nicht nur Schaltflächen wieder ein. Auch das Schließen des Formulars und Timer kommen herein. Fehler wie zerstörte Daten während der Verarbeitung oder eine Ausnahme durch das Anfassen eines geschlossenen Formulars sind zeitabhängig und schwer zu reproduzieren und oft schwieriger zu untersuchen als „Keine Rückmeldung“.

Das Prinzip ist, die Arbeit zu trennen, nicht die Pumpe von Hand zu drehen. Manuelle Nachrichtenverarbeitung bleibt auf begrenzte Strukturen wie einen modalen Fortschrittsdialog beschränkt. Für gewöhnliche langwierige Arbeit nutzen Sie die Trennung und die Wiedereintrittsvermeidung aus Kapitel 5.

6.2 Geisterfenster zu deaktivieren bringt die Bedienung nicht zurück

DisableProcessWindowsGhosting ist eine API, die den Tausch gegen ein Geisterfenster für den aufrufenden Prozess deaktiviert. Die Deaktivierung gilt für die Lebensdauer des Prozesses.4

Sie ist für besondere Zwecke gedacht, etwa Kioskterminals, bei denen Sie vermeiden wollen, dass das Betriebssystem ein bedienbares Ersatzfenster aufstellt. Die Tatsache, dass die Nachrichtenverarbeitung steht, ändert sich nicht, und der Benutzer verliert auch den Weg zum Verschieben oder Beenden, den das Geisterfenster bot. Für die Gegenmaßnahme gegen „Keine Rückmeldung“ in gewöhnlichen Apps ist sie nicht gedacht.

Methode Was sich ändert Was bleibt
Die Pumpe von Hand mit DoEvents oder Ähnlichem drehen Auch mitten in der Verarbeitung andere Nachrichten behandeln Beliebige Ereignisse treten wieder ein und können den Zustand zerstören
DisableProcessWindowsGhosting Stoppt den Tausch gegen ein Ersatzfenster durch das Betriebssystem Der UI-Thread bleibt blockiert
Zeitaufwendige Arbeit von der UI trennen Lässt den UI-Thread zu Eingabe und Zeichnen zurückkehren Fortschritt, Abbruch und Wiedereintrittsvermeidung müssen ebenfalls entworfen werden

7. Untersuchungsverfahren: Den hängenden Moment festhalten, bevor Sie schließen

7.1 Zuerst einen Dump nehmen

Bei der Untersuchung von „es hängt gelegentlich“ ist das Wertvollste der Threadzustand im hängenden Moment selbst. Sobald Sie beenden oder neu starten, ist dieser Zustand verloren.

Klicken Sie auf der Registerkarte „Details“ des Task-Managers mit der rechten Maustaste auf den Zielprozess und wählen Sie „Abbilddatei erstellen“. Damit erhalten Sie einen vollständigen Dump mit den Stacks aller Threads. Teilen Sie den Leuten, die die Anfragen entgegennehmen, ebenfalls das Verfahren mit: „Wenn es hängt, vor dem Schließen einen Dump nehmen.“

Zum Aufbau der Sammlung siehe den Artikel zum Sammeln von Absturzabbildern.

7.2 Den Thread ansehen, dem das hängende Fenster gehört

Öffnen Sie den Dump in WinDbg und prüfen Sie den Stack des UI-Threads. Der Thread, der die Nachrichtenschleife dreht, ist gewöhnlich Thread 0, aber es gibt Apps mit mehreren UI-Threads, also entscheiden Sie nicht nur nach der Nummer; sehen Sie den Thread an, dem das hängende Fenster gehört.

Was auf dem Stack zu sehen ist Was als Nächstes zu prüfen ist
Warten in ReadFile oder einer Netz-API Dateizugriff, synchrones I/O, Warten auf eine Netzantwort
Warten in einer WaitFor…-Funktion Die Sperre oder das Synchronisierungsobjekt. Auf wessen Abschluss gewartet wird und was die Gegenseite tut
Warten innerhalb von SendMessage Ob der Zielthread Nachrichten verarbeiten kann. Ob sie gegenseitig warten

Auf dem Stack des UI-Threads steht, worauf er wartet. Bei Verdacht auf Deadlock gleichen Sie die Warteziele beider Threads ab, nicht nur eines. Wie man sie liest, erklärt der einführende WinDbg-Artikel.

Grundverfahren zur Untersuchung von Keine RückmeldungIm hängenden Moment einen Dump nehmen, den Stack des UI-Threads ansehen, feststellen, ob er in synchronem I/O, einem Sperrwarten oder SendMessage zwischen Threads steckt, und das zur passenden Entwurfskorrektur führenDer hängende MomentDump nehmen (vor dem Schließen)Den Stack des UI-Threads ansehenSynchrones I/O oder NetzwartenSperrwartenSendMessage zwischen ThreadsDie betreffende Stelle auf einen Worker auslagern

Abbildung 4: Vom Dump des hängenden Moments aus verfolgen, worauf der UI-Thread wartet. Bei I/O asynchron machen oder auslagern; bei Sperren und SendMessage auch die Warte-Struktur prüfen.

7.3 Live-Prüfung und Zeitreihenanalyse unterscheiden

Neben Dumps gibt es einen Weg, einen laufenden Prozess an Ort und Stelle anzusehen, und einen Weg, den Zeitverlauf aufzuzeichnen.

Was Sie herausfinden wollen Methode
Den hängenden Moment festhalten und später untersuchen Einen Dump nehmen und in WinDbg analysieren
Threads und Stacks eines lebenden Prozesses an Ort und Stelle sehen Process Explorer nutzen
Ständige Trägheit oder das Warten des UI-Threads über die Zeit verfolgen Eine Aufzeichnung mit WPR nehmen und in WPA analysieren

Wie man eine Zeitreihe aufnimmt, behandelt WPR/WPA in der Praxis.

7.4 Kandidaten vom Symptom eingrenzen und mit Belegen bestätigen

Reproduktionsbedingungen sind ebenfalls ein Einstieg in die Untersuchung. Entscheiden Sie die Ursache jedoch nicht nur vom Symptom; gleichen Sie sie mit Dumps und Aufzeichnungen ab.

Symptom Zuerst verdächtigen Wo zu prüfen ist
Hängt bei einer bestimmten Operation immer Synchrones I/O oder Ähnliches in diesem Handler Die Verarbeitung zu dieser Operation und der Stack des UI-Threads
Hängt selten, ohne Zusammenhang mit einer Operation Sperrreihenfolge oder ein Deadlock durch SendMessage zwischen Threads Die Stacks beider Threads, die aufeinander warten
Hängt nur in einer bestimmten Umgebung Warten und Timeouts durch Netzlaufwerke, Proxys, Antivirensoftware und Ähnliches Die API, auf die gewartet wird, und ihre Antwortzeit in dieser Umgebung

8. Zusammenfassung

„Keine Rückmeldung“ ist ein Mechanismus, bei dem Windows feststellt, dass die Nachrichtenverarbeitung eines Fensters stehen geblieben ist, und einen Ersatzbildschirm aufstellt. Die Einheit der Beurteilung ist das Fenster und der GUI-Thread, dem es gehört, nicht der Prozess als Ganzes. Das interne Timeout von 5 Sekunden ist ein Wert, der sich in Zukunft ändern kann, und ist von einer angenehmen UI-Antwortzeit zu unterscheiden.12

Zu beheben ist nicht die Anzeige, sondern die Berechnung oder das Warten, die den UI-Thread lange belegen. CPU-Arbeit und APIs, die nur synchron existieren, gehen auf den Worker, asynchrones I/O auf asynchrone APIs, und Bildschirmaktualisierungen kehren auf den UI-Thread zurück. Fortschritt, Abbruch und Wiedereintrittsvermeidung entwerfen Sie als Set. Das Umgehen mit DoEvents oder das Deaktivieren von Geisterfenstern ist kein Ersatz dafür.

Wenn die Ursache unbekannt ist, beginnen Sie damit, vor dem Beenden einen Dump zu nehmen und zu sehen, worauf der Thread wartet, dem das hängende Fenster gehört. Ersetzen Sie „es sieht kaputt aus“ durch „der UI-Thread kann nicht zur Nachrichtenverarbeitung zurückkehren“, und Untersuchungsorte und Entwurfskorrekturen hängen zusammen.

Weiterführende Artikel

Zugehörige Beratungsfelder

KomuraSoft LLC übernimmt Ursachenuntersuchungen (Dump-Analyse und Trace-Analyse) von Geschäftsanwendungen, die „gelegentlich hängen“ oder auf „Keine Rückmeldung“ gehen, das Umbauen von Legacy-UI-Code voller synchroner Verarbeitung auf async/await und Worker-Thread-Trennung sowie Reviews von UI-Entwürfen, die nicht einfrieren. Auch wenn das Reproduktionsverfahren noch unbekannt ist, können wir ab dem Entwurf helfen, wie Belege gesammelt werden.

Quellen

  1. Microsoft Learn, IsHungAppWindow function (winuser.h). Zum Urteilskriterium, dass eine App als ohne Rückmeldung behandelt wird, wenn sie „nicht auf Eingabe wartet, nicht in der Startverarbeitung ist und PeekMessage für das interne Timeout von 5 Sekunden nicht aufgerufen hat“; dazu, dass dieses 5-Sekunden-Kriterium sich ändern kann; sowie dazu, dass die Funktion für ein Geisterfenster immer TRUE zurückgibt. ↩ ↩2 ↩3 ↩4

  2. Microsoft Learn, GetMessage function (winuser.h). Dazu, dass das System ein Fenster der obersten Ebene als ohne Rückmeldung behandelt, wenn es mehrere Sekunden auf Nachrichten nicht antwortet, und es durch ein Geisterfenster derselben Z-Reihenfolge, Position, Größe und Erscheinung ersetzt; dazu, dass der Benutzer es nur verschieben, in der Größe ändern oder schließen kann; sowie dazu, dass ein Geisterfenster nicht angelegt wird, während ein Debugger verbunden ist. ↩ ↩2 ↩3 ↩4 ↩5

  3. Microsoft Learn, How to make thread-safe calls to controls (Windows Forms). Dazu, dass WinForms-Steuerelemente nicht sicher von einem anderen Thread als dem, der sie angelegt hat, angefasst werden dürfen; zur Nutzung von Invoke/BeginInvoke für Aktualisierungen von einem anderen Thread; sowie zu sicheren asynchronen Mustern mit async/await oder BackgroundWorker. ↩ ↩2 ↩3 ↩4

  4. Microsoft Learn, DisableProcessWindowsGhosting function (winuser.h). Dazu, dass Sie für den aufrufenden GUI-Prozess die Geisterfensterfunktion deaktivieren können, die ein Fenster ohne Rückmeldung minimierbar, verschiebbar und schließbar macht; sowie dazu, dass die Deaktivierung für die Lebensdauer des Prozesses gilt. ↩ ↩2

  5. Microsoft Learn, About Messages and Message Queues. Dazu, dass Windows-Apps ereignisgetrieben sind und die Fensterprozedur Nachrichten verarbeitet; zur Unterscheidung zwischen eingereihten Nachrichten und direkt gesendeten Nachrichten; zum Tausch eines Fensters ohne Rückmeldung gegen ein Geisterfenster; sowie zum Abschnitt über Deadlock durch Threads, die einander Nachrichten senden. ↩ ↩2 ↩3

  6. Microsoft Learn, Using Messages and Message Queues. Zu einer typischen Umsetzung einer Nachrichtenschleife mit GetMessage, TranslateMessage und DispatchMessage sowie dazu, wie man eine Nachrichtenwarteschlange prüft. ↩

  7. Microsoft Learn, SendMessage function (winuser.h). Dazu, dass SendMessage die Fensterprozedur des angegebenen Fensters aufruft und nicht zurückkehrt, bis die Verarbeitung fertig ist; dazu, dass ein Senden an ein Fenster auf einem anderen Thread den Sender warten lässt, bis dieser Thread die Nachricht verarbeitet; sowie zum Unterschied zu PostMessage, das die Nachricht auf die Warteschlange legt, ohne auf eine Antwort zu warten. ↩ ↩2 ↩3 ↩4

  8. Microsoft Learn, SendMessageTimeout function (winuser.h). Dazu, eine Nachricht mit einem Timeout senden zu können; sowie zu einem Flag (SMTO_ABORTIFHUNG), das ohne Warten zurückkehrt, wenn das Fenster keine Rückmeldung gibt (als hängend beurteilt wurde). ↩

Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.

Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.

Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.

Häufige Fragen

Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.

Unter welchen Bedingungen erscheint „Keine Rückmeldung“?
Das Betriebssystem beurteilt ein Fenster als ohne Rückmeldung, wenn eine App mit Fenster nicht auf Eingabe wartet, nicht in der Startverarbeitung ist und 5 Sekunden lang keine Nachricht abgeholt hat (PeekMessage). Das so beurteilte Fenster der obersten Ebene wird ausgeblendet und durch ein „Geisterfenster“ derselben Position, Größe und Erscheinung ersetzt. Der Titeltext „(Keine Rückmeldung)“ und das milchig-weiße Aussehen gehören zu diesem Geisterfenster, das nur Verschieben, Minimieren und Schließen erlaubt. Mit anderen Worten: „Keine Rückmeldung“ ist nichts, was die App selbst anzeigt — es ist ein Bildschirm, den das Betriebssystem an ihrer Stelle aufstellt.
Gibt es eine Einstellung, damit „Keine Rückmeldung“ während laufender Arbeit nicht erscheint?
Der Aufruf von DisableProcessWindowsGhosting deaktiviert den Tausch gegen ein Geisterfenster für diesen Prozess. Das macht den Hänger für den Benutzer nur weniger sichtbar — das Fenster reagiert trotzdem nicht auf Eingabe, und aus Benutzersicht ist es ein vollständiges Einfrieren ohne Weg zum Verschieben oder Schließen. Die eigentliche Lösung ist nicht, die Anzeige zu unterdrücken, sondern schwere Arbeit auf einen Worker-Thread zu verschieben, sodass der UI-Thread nie auch nur eine Zehntelsekunde blockiert ist, geschweige denn fünf. Beachten Sie außerdem, dass das Betriebssystem kein Geisterfenster anlegt, während ein Debugger verbunden ist, sodass es so aussehen kann, als geschähe „Keine Rückmeldung während des Debuggens nie“.
Ist es akzeptabel, „Keine Rückmeldung“ mit DoEvents zu vermeiden (die Nachrichtenschleife manuell zu pumpen)?
Es wird nicht empfohlen. DoEvents oder eine PeekMessage-Schleife mitten in schwerer Arbeit umgeht das Urteil ohne Rückmeldung, aber jeder Ereignishandler kann dann mitten in der Arbeit wieder eintreten — ein zweiter Klick auf die Schaltfläche, das Schließen des Fensters, ein Timer und so weiter. Wiedereintrittsfehler wie ein anderer Handler, der Daten umschreibt, die noch verarbeitet werden, oder ein Formular anfasst, das bereits geschlossen sein sollte, und eine Ausnahme wirft, sind zeitabhängig und schwer zu reproduzieren — und lästiger als „Keine Rückmeldung“ selbst. Der richtige Ansatz ist, die Arbeit selbst mit Task.Run oder Ähnlichem auf einen Worker-Thread zu verschieben und den UI-Thread nur für die Fortschrittsanzeige und das Annehmen von Abbruch verantwortlich zu lassen.
Wie aktualisiere ich den Bildschirm (Steuerelemente) von einem Worker-Thread?
WinForms-Steuerelemente und WPF-Elemente dürfen nur von dem Thread angefasst werden, der sie angelegt hat (normalerweise der UI-Thread). Sie direkt von einem Worker-Thread anzufassen verursacht Ausnahmen oder undefiniertes Verhalten. In C# ist async/await der einfachste Weg: Die Fortsetzung nach await kehrt zum aufrufenden UI-Thread zurück, sodass Sie Steuerelemente nach dem await normal aktualisieren können. Um ausdrücklich zu wechseln, nutzen Sie Control.Invoke/BeginInvoke in WinForms und Dispatcher.InvokeAsync in WPF. In nativem Win32 ist das etablierte Muster, dass der Worker-Thread dem UI-Thread mit PostMessage eine eigene Fertigstellungsnachricht sendet und die Fensterprozedur den Bildschirm aktualisiert.
Wie untersuche ich die Ursache einer App, die „Keine Rückmeldung“ zeigt?
Das Wichtige ist, den Zustand im hängenden Moment selbst festzuhalten. Nehmen Sie zuerst einen vollständigen Dump über die Registerkarte Details des Task-Managers mit „Abbilddatei erstellen“, und schauen Sie dann in WinDbg auf den Stack des UI-Threads (des Threads, der die Nachrichtenschleife ausführt). Ob er in synchronem I/O, einem Netzwarten, einem Sperrwarten oder dem Warten auf einen anderen Thread über SendMessage steckt, zeigt sich auf dem Stack so, wie es ist. Um einen lebenden Prozess anzusehen, nutzen Sie die Thread-Liste und die Stack-Ansicht von Process Explorer; um es über die Zeit zu verfolgen, erfassen Sie eine Aufzeichnung mit WPR. Siehe auch den einführenden WinDbg-Artikel dieser Site, Process Explorer in der Praxis und WPR/WPA in der Praxis.

Autorenprofil

Profilseite des Artikelautors.

Go Komura

Geschäftsführer von KomuraSoft LLC

Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.

Zurück zum Blog