AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Anwendung bleibt komplett stehen

Ein Thema von Phoenix · begonnen am 10. Sep 2007 · letzter Beitrag vom 10. Sep 2007
Antwort Antwort
Benutzerbild von Phoenix
Phoenix
(Moderator)

Registriert seit: 25. Jun 2002
Ort: Hausach
7.606 Beiträge
 
#1

Anwendung bleibt komplett stehen

  Alt 10. Sep 2007, 15:43
Ich baue gerade eine Visualisierung für eine SPS.
Dazu müssen einige Controls synchron im Sekundentakt blinken. Hierfür benutze ich einen Timer, der, solange das Formular das aktive Form der Anwendung ist, alle Sekunde das Zeichnen auslöst.

Dazu benutze ich folgendes Konstrukt:
Delphi-Quellcode:
// aus der Redraw-Routine
      Application.ProcessMessages;

      //*** change Flash State every time we refresh ourself:
      FControlFlashState := not FControlFlashState;

      //*** stop redrawing
      SendMessage(pnBaseForm.Handle, WM_SETREDRAW, Integer(FALSE), 0);

      //*** update controls:
      for i := 0 to Self.ComponentCount - 1 do
      begin
        if Self.Components[i] is TCustomVisuControl then
        begin
          (Self.Components[i] as TCustomVisuControl).ChangeState(FControlFlashState);
        end;
      end;

      //*** enable und force redrawing
      SendMessage(pnBDMBaseForm.Handle, WM_SETREDRAW, Integer(TRUE), 0);
      InvalidateRect(pnBDMBaseForm.Handle, nil, false);

      //*** force update of controls:
      for i := 0 to Self.ComponentCount - 1 do
      begin
        if Self.Components[i] is TControl then
        begin
          (Self.Components[i] as TControl).Repaint;
        end;
      end;
Im ChangeState des Controls schaut das Control erstmal auf einer Memtable nach, ob für es neue Daten vorliegen. Dann holt es für diese Daten die Farben und ggf, eine Caption ab, und zwar für den Flashstate (wenn ein Control z.B. rot/weiss blinken soll bei Flashstate true -> rot und bei Flashstate false -> weiss).

Da sich das Control beim setzen von Color, Font.Color und/oder Caption ja sonst sofort neu zeichnen würde und das Abholen der Daten auch ein bisschen Zeit braucht, würden die Controls nicht synchron blinken. Deshalb der Behelf mit WM_SETREDRAW.

Das pnBaseForm ist ein Panel, welches auf einem WinControl sitzt welches auf einem Form sitzt welches aber wiederum (ähnlich wie ein Frame) mit noch einigen Controls dazwischen auf dem Mainform sitzt. Das ist durch die Anwendung so vorgegeben, daran kann ich auch nichts ändern.

Das Problem:
Wenn sich das Formular gerade in dieser Routine befindet, und man in diesem Moment aus der Anwendung hinausklickt, dann bleibt die Anwendung stehen. Komplett. Zumindest glaube ich, dass sich die Anwendung genau in dieser Routine befindet, wenn es passiert.

Die CPU-Last geht auf Null Prozent zurück und die Anwendung reagiert gar nicht mehr. Im Debugger kann ich die Ausführung auch nicht mehr anhalten um zu gucken wo er hängt. Die Anwendung muss ich mit dem Taskmanager abschiessen.

Setze ich den Timer auf 5 Sekunden ist das Verhalten schwieriger Nachzuvollziehen - weil der Timer eben nicht so oft tickt und man den richtigen Augenblick abpassen muss.

Was kann ich gegen diesen Totalhänger tun?
Sebastian Gingter
Phoenix - 不死鳥, Microsoft MVP, Rettungshundeführer
Über mich: Sebastian Gingter @ Thinktecture Mein Blog: https://gingter.org
  Mit Zitat antworten Zitat
Muetze1
(Gast)

n/a Beiträge
 
#2

Re: Anwendung bleibt komplett stehen

  Alt 10. Sep 2007, 15:49
Schonmal ausprobiert es bei einem InvalidateRect() zu belassen und danach nicht ein repaint aufzurufen? Repaint ist eine Kapslung von Invalidate + UpdateWindow() und somit sogar ein doppelter Code.

Auch verhindern das WM_SETDRAW das Zeichnen, aber alle zu zeichnenden Bereiche werden gemerkt und akkumuliert. Von daher sollte das zurücknehmen von WM_SETDRAW ein neuzeichnen auslöen ohne dass du vorher nochmal die einzelnen Elemente invalidieren musst.

Falls nicht, dann würde folgendes reichen:
Delphi-Quellcode:
      //*** enable und force redrawing
      SendMessage(pnBDMBaseForm.Handle, WM_SETREDRAW, Integer(TRUE), 0);
      InvalidateRect(pnBDMBaseForm.Handle, nil, false);

      //*** force update of controls:
      UpdateWindow(pnBDMBaseForm.Handle);
  Mit Zitat antworten Zitat
Benutzerbild von Phoenix
Phoenix
(Moderator)

Registriert seit: 25. Jun 2002
Ort: Hausach
7.606 Beiträge
 
#3

Re: Anwendung bleibt komplett stehen

  Alt 10. Sep 2007, 16:00
Zitat von Muetze1:
Auch verhindern das WM_SETDRAW das Zeichnen, aber alle zu zeichnenden Bereiche werden gemerkt und akkumuliert. Von daher sollte das zurücknehmen von WM_SETDRAW ein neuzeichnen auslöen ohne dass du vorher nochmal die einzelnen Elemente invalidieren musst.
Tut es aber nicht. Wenn ich nicht jedes einzelne Control Invalidate/Update bzw. eben Repaint aufrufe, werden die Controls NUR dann neu gezeichnet, wenn sie danach nochmal irgendwann verdeckt wurden. Das heisst, die controls blinken nicht, sondern behalten ihre alte Farbe, wenn ich kein Inavlidate aufrufe, nachdem ich SETREDRAW TRUE gesendet habe.

Gerade getestet: UpdateWindow reicht auch nicht. Ich muss explizit jedes einzelne Control invalidieren, sonst wechselt sich die Farbe nicht.

Aber das Problem liegt auch nicht daran, sondern an den zwei WM_SETREDRAW Messages. Nehme ich die raus, kann ich aus der Anwendung raus und wieder reinklicken so oft ich will und wann ich will: Kein Problem. Sobald die beiden Messages drin sind bleibt die Anwendung stehen sobald in rausklicke wenn er dazwischen ist.

Jetzt die Frage: WARUM bleibt die Anwendung überhaupt stehen (es sieht ja fast so aus, als würde die MessageLoop komplett blockieren), und viel wichtiger: Was kann ich dagegen tun?

Die beiden Messages einfach weglassen ist keine Option, denn kann man tatsächlich jedem einzelnen Control beim Neuzeichnen zugucken, und das erste Control blinkt alles andere als synchron mit dem letzten.
Sebastian Gingter
Phoenix - 不死鳥, Microsoft MVP, Rettungshundeführer
Über mich: Sebastian Gingter @ Thinktecture Mein Blog: https://gingter.org
  Mit Zitat antworten Zitat
shmia

Registriert seit: 2. Mär 2004
5.508 Beiträge
 
Delphi 5 Professional
 
#4

Re: Anwendung bleibt komplett stehen

  Alt 10. Sep 2007, 16:06
Wie wäre es damit (Ersatz für Application.ProcessMessages):
Delphi-Quellcode:
Procedure ProcessPaintMessages;
Var
   Msg : TMsg;
Begin
   While PeekMessage(Msg, 0, WM_PAINT, WM_PAINT, PM_REMOVE) Do
   Begin
// TranslateMessage(Msg); // translate only Keyboard-Messages
      DispatchMessage(Msg);
   End;
End;
Damit werden nur Paint-Messages verarbeitet; alle anderen Messages verbleiben in der Queue.
Andreas
  Mit Zitat antworten Zitat
Benutzerbild von Phoenix
Phoenix
(Moderator)

Registriert seit: 25. Jun 2002
Ort: Hausach
7.606 Beiträge
 
#5

Re: Anwendung bleibt komplett stehen

  Alt 10. Sep 2007, 16:13
Den hab ich eigentlich absichtlich da drin, damit die MessageQueue komplett leer ist, bevor ich diese Updateroutine beginne. Ohne das Ding fühlt sich die Anwendung richtig zäh an, und lässt sich auch nicht richtig bzw. nur extrem ruckelig verschieben.
Sebastian Gingter
Phoenix - 不死鳥, Microsoft MVP, Rettungshundeführer
Über mich: Sebastian Gingter @ Thinktecture Mein Blog: https://gingter.org
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#6

Re: Anwendung bleibt komplett stehen

  Alt 10. Sep 2007, 16:30
Und das Projekt tut sich nicht zufällig mit dem application.processmessages innerhalb des Timers selbst verschlucken?
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
Benutzerbild von Phoenix
Phoenix
(Moderator)

Registriert seit: 25. Jun 2002
Ort: Hausach
7.606 Beiträge
 
#7

Re: Anwendung bleibt komplett stehen

  Alt 10. Sep 2007, 16:34
Hatte ich schon getestet. Ich habe den beim Beginn der Prozedur deaktiviert, das Ding in einen Try - finally Block geklemmt und den Timer hinterher wieder aktiviert, so dass das Ereignis nicht kommen konnte, während es da drin war. Anmerkung dazu: Timer deaktivieren bedeutet auf diesen Formularen, dass die Timer-Komponente vernichtet wird. Beim Aktivieren des Autorefreshs wird der Timer dann neu Erzeugt. Also ist das wirklich sicher gewesen. Geholfen hats allerdings nix.
Sebastian Gingter
Phoenix - 不死鳥, Microsoft MVP, Rettungshundeführer
Über mich: Sebastian Gingter @ Thinktecture Mein Blog: https://gingter.org
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:51 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz