Das "null"-Fenster neu zeichnen und seine Konsequenzen


Als ich jetzt neulich das Demo-Programm zu meinem Artikel Painting only when your window is visible on the screen geschrieben habe, ist mir ein kleiner aber Folgen schwerer Fehler unterlaufen - nun ja, mehr oder weniger Folgen schwer. Und zwar bei der TimerCallback Prozedur:

procedure TimerCallback(hWnd: THandle; uMsg: UINT; IDTimer: UINT; dwTime: DWORD); stdcall;
begin
  KillTimer(hWnd, IDTimer);
  InvalidateRect(hWnd, @TimeRect, False);
end;

Mein Fehler war, dass ich sie nicht mit stdcall deklariert hatte. stdcall bezeichnet die Aufrufkonvention, wie die Funktion aufgerufen werden soll / muss. Also in welcher Reihenfolge die Parameter übergeben werden und wer letzendlich dafür verantwortlich ist den Stack wieder aufzuräumen, der Caller oder die aufgerufene Funktion. Aber das nur am Rande. Ergebnis war letztendlich, dass das übergebene Handle ungültig war und kein existierendes Fenster identifiziert hatte. Was wiederum dazu geführt hatte, dass der gesamte Desktop für ungültig erklärt wurde, Windows alle sichtbaren Toplevel Fenster dazu veranlasst hat, sich neu zu zeichen und als Resultat dieser Aktion hat der gesamte Desktop geflackert. Zu diesem Zeitpunkt wußte ich nicht, was da passiert bzw. warum es passiert. Was auch ein Grund war, warum ich nicht daraufgekommen bin, was da falsch läuft. Wenn man mal davon absieht, dass ich das stdcall regelmäßig vergesse und dann wieder mal ein verzweifeltes Posting in der Delphipraxis absetze, um mich dann mit hoch rotem Kopf unter meinem Schreibtisch zu verkriechen. Also was passiert da und warum?

Die API Funktion InvalidateRect benachrichtigt den Fenstermanager darüber, wenn die Pixel eines bestimmten Fensters nicht mehr aktuell sind und neu gezeichnet werden müssen. Und wenn man jetzt 0 oder, wie in meinem Fall, ein ungültiges Handle übergibt, dann wird dies als ein Sonderfall behandelt um kompatibel zu alten Windows Versionen zu bleiben und es werden alle sichtbaren Fenster für ungültig erklärt und als Konsequenz daraus neu gezeichnet, was dann das Flackern verursacht.

Wäre mir das früher bekannt gewesen, hätte ich aus dem resultierenden Verhalten darauf schließen können, dass das Handle ungültig sein muss und irgendwas bei der Parameterübergabe schiefläuft. Jedenfalls, was ich damit sagen will, etwas Hintergrundwissen über die Dinge, die man da macht, kann nie schaden. Deswegen machen mich meine mangelden ASM Kenntnisse auch etwas traurig. Allerdings frage ich mich, was aus der Generation von Programmierer werden soll, die unter dem .NET Framework aufwächst und nichts anderes mehr kennt.

Mein Dank für die Aufklärung geht übrigens wieder an Raymond Chen, dessen Blog bezüglich solcher Dinge eine ware Fundgrube ist.

2010-12-29T23:44:49 +0100, mail+homepage[at]michael-puff.de