Legacy Code — Exe schläft nach Kaltstart

Endlich kam eine interessante Aufgabe. Eine Aufgabe, die typisch für Legacy Code war. Die schon in die Jahre gekommene Anwendung zeigte mit jeder neuen Version einen schlechteren Kaltstart. Wenn sie zum ersten Mal gestartet wurde, fror sie für einige Zeit ein. Diese Zeit wurde von Version zu Version immer länger. In der aktuellen Version konnten es mehrere Minuten sein.

Zwar hatten sich die Anwender schon an einen Verzug gewöhnt. Nun sollte wenigstens ein Grund für diesen Verzug gefunden werden. Eine Behebung sollte anschließend geplant werden.

Wie freute ich mich auf diese Aufgabe!

Das Verhalten nach dem Start

Es fing an, wie das Hornberger Schießen. Die Anwendung zeigte zügig den Rahmen und setzte sich mit anderen Komponenten in Verbindung. Zunächst prüfte es den Terminkalender. Wie erwartet, zeigte es „kein Termin steht an“ und der Benutzer konnte loslegen.

Noch nicht einmal eine Minute dauerte die ganze Aktion. Ich schloss die Anwendung und bootete den Rechner erneut.

Im zweiten Kaltstart zeigte sich die Herausforderung. Diesmal bewegte ich die Maus über die Menüleiste. Kein Eintrag färbte sich. Das Fenster ließ sich nicht bewegen und auch nicht verändern.

Das war also das „Einfrieren“. Nach einigen Minuten ging es weiter.

Insgesamt zog ich an dem Vormittag fünf Protokolle und Logs. Wäre doch gelacht, wenn das nicht eine eindeutige Lösung hätte.

Wird es durch einen Prozess geblockt?

Mein erster Gedanke war ein Block im Message Handler. Das kann schon passieren, wenn eine Funktion aufgerufen wird, die synchron einen Dienst aufruft und auf Antwort wartete. In den Messpunkten sollte das zu sehen sein.

Diese Messpunkte schrieben Anfang und Ende ausgewählter Funktionen in einen shared memory Ringspeicher. Dieser schickt auch aus dem Feld nach Hause, wenn die Kunden das erlaubten. Da alle Systeme in den einen Buffer mit Zeitstempel schrieben, sollte hier eine lange Funktion, die mehrere Minuten brauchte, zu finden sein.

Leider war dem gar nicht so. Es schien als schliefen alle Teile im System für einige Minuten. Alle fünfzehn Sekunden zeigte sich ein Timer im System. Mehr war nicht zu sehen.

Mir fiel auf, dass im Rahmen keine Meldung der Art “not responding” oder “antwortet nicht” zu sehen war. Wenn ich auf die Anwendung klickte, zeigte sich auch nie eine Sanduhr. Sie wurde auch nicht grau.

Das schien es nicht zu sein. Eine weitere Hypothese musste her.

Ein modaler Dialog im Hintergrund

Ich war ein wenig ratlos und beschloss den Source der Anwendung von Adam und Eva aus zu studieren. Bei MFC fängt alles mit InitInstance der Applikation an. Diese Methode zeichnete sich durch eine umfangreiche Länge aus. In dem Modul fand ich auch ein PreTranslateMessage und Keyboard Hooks. Was hatten meine Vorgänger nicht alles eingebaut!

Ein Kommentar bezog sich auf eine Situation, in der die Anwendung „unbedienbar“ erscheinen konnte. Ich verstand, dass bei Validierungen manchmal ein Dialog unter dem Hauptfenster angezeigt wurde. Mit einem ausgeklügelten Mechanismus schickte die Anwendung immer mal wieder ein Keyboard Enter an ein Windows handle.

Das war die nächste Hypothese. Beim Start der Anwendung passiert recht viel. Vielleicht gab es bei Kaltstart eine Plausibilität, die nicht stimmte. Ein Dialog setzt sich unter das Hauptfenster und dieses Fenster bekommt dann gegebenenfalls ein OK, sodass es schließt und dann ist die Wartezeit vorbei.

Im Log war ein Eintrag über eine Progressdialog, der sich verabschiedete. War das die Stelle?

Das konnte nicht sein.

Threads verbunden

Aber die Zeilen dahinter hatten es in sich.

Im Kommentar stand etwas von Windows Vista und einem Trick, mit dem die Anwendung in der Vorgrund käme. Der Thread des aktuellen Vordergrundfensters wird mit dem Thread des neu gestarteten Hauptfenster verheiratet. Die Methode dafür nannte sich AttachThreadInput.

Das kam mir suspekt vor und ich fragte bei Google, ob es hier nicht Probleme geben könnte.

In der Tat gab es hierzu einen Beitrag im Devblog von Microsoft.

Das Beispiel mit dem Hänger waren genau die Zeilen aus dem Source!

Allerdings fehlte die Erklärung, warum das denn so sei. Der Blogbeitrag verwies auf ein Video, das ich mir nicht antun wollte. Zum Glück fand ich dann noch die Folien zu dem Vortrag.

Jede Meldung muss nun von zwei Threads bearbeitet werden. Wenn der eine hängt, hängt auch der andere.

Das war dann die große Lösung, die sich im Test auch bestätigte.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.