Entwickler-Ecke

Windows API - Schließbutton in der Titelzeile


Mathematiker - So 05.01.14 20:56
Titel: Schließbutton in der Titelzeile
Hallo,
bei dem einen oder anderen Programm möchte ich eine schnell laufende Animation erreichen und verzichte deshalb auf einen Timer.
Konkret läuft in einer Methode die Animation in der Form:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
procedure animation(sender:tobject);
begin
    if animationlaeuft then
    begin
       animationlaeuft:=false;
       exit;
    end
    else
    begin
       animationlaeuft:=true;
       repeat
         ... //Animation
         application.processmessages; //im Normalfall nicht bei jedem Durchlauf
         ...  
       until not animationlaeuft;
    end;
end;

procedure schliessen(sender:tobject);
begin
    if animationlaeuft then animationlaeuft:=false;
    close;
end;

Sobald die Variable animationlaeuft auf false gesetzt wird (evtl. Buttonklick) stoppt die Animation. Das funktioniert auch perfekt mit der Methode schliessen, die dann auch das Programm beendet.

Nun mein Problem: Wie kann ich erreichen, dass der Standard-Schließbutton in der Titelzeile ebenfalls die Animation stoppt und das Programm beendet?
Meine Versuche, in die Ereignisse Onclose oder Onclosequery die zwei Zeilen von schliessen einzutragen, funktionieren nicht. Während der Animation wird scheinbar keines dieser Ereignisse durch den Schließbutton ausgelöst. Ich kann soviel ich will auf den Schließbutton drücken, es passiert nichts. Beende ich mit der Methode schliessen ist dagegen alles in Ordnung.

Welches Ereignis wird denn ausgelöst, wenn ich auf den Schließbutton drücke?
Ich bin mir nicht ganz sicher, aber kann es sein, dass es unter Vista und XP noch funktionierte? Irgendwie komisch.

Beste Grüße
Mathematiker

Nachtrag: Hat sich eigentlich erledigt. Ich habe es hingekriegt mit

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
procedure TForm.WndProc(var msg:TMessage);
begin
    case Msg.Msg of
      WM_CLOSE: begin
                  if sofortabbruch=false then sofortabbruch:=true;
                  close;
                end  
    Else
      inherited;
    end;
end;

Aber vielleicht hat jemand ja noch eine bessere Idee.


Delphi-Laie - So 05.01.14 21:18

Ich kann da leider nur aus bescheidenen Erfahrungen schöpfen.

Mit Application.Processmessages nimmt ein auch im VCL-Thread vollbeschäftigtes Formularprogramm Eingaben wahr und löst die entsprechende(n) Ereignisbehandlungsroutinen aus. Dort kann man irgendeine Variable umschalten. Diese Variable wird in der eigentlichen Berechnung (nahezu) ständig ausgewertet - so eine Art Polling (kein Eingabe-, sondern ein Variablenpolling). So läuft es in meinem "Sortierkino".

Oder man agiert mit einem Extrathread, aber das ist aufwendiger (synchronize bei Formularausgaben). Den kann man sich selbst separat beenden lassen oder von außen gewaltsam abschießen (s. mein unfertiger "Langzahlentaschenrechner"), aber das ist unelegant.

Edit: Da waren wir wohl im Geschwindigkeitswettstreit, ohne daß wir es wußten.

Edit 2: Beim X-Druck werden OnClose und OnCloseQuery ausgelöst, die Reihenfolge weiß ich jetzt aber nicht (einfach mal mit z.B. Messagesboxen oder verschiedenfrequenten Pieptösnen ausprobieren).


mandras - So 05.01.14 22:13

Wenn Du im OnClick einer Schaltfläche CLOSE aufrufst,
wird über Zwischenwege Application.Terminate aufgerufen, das sendet an Windows das PostQuitMessage, die Anwendung wird von Windows beendet.

Drückst Du die Schaltfläche "Schließen" oben rechts im Fenstertitel passiert das nicht.
Zwar merkt Delphi vor, daß die Anwendung geschlossen werden soll, bleibt aber in der repeat-Schleife mit dem Processmessages "kleben".

Abhilfe:


Delphi-Quelltext
1:
until not animationlaeuft or Application.Terminated;                    


Mathematiker - So 05.01.14 22:26

Hallo,
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Beim X-Druck werden OnClose und OnCloseQuery ausgelöst, die Reihenfolge weiß ich jetzt aber nicht (einfach mal mit z.B. Messagesboxen oder verschiedenfrequenten Pieptösnen ausprobieren).

Danke für den Tipp. Nur leider funktioniert es nicht.
Läuft die Animation, so bewirkt das Klicken auf X kein "Hineinspringen" in die Methoden von beiden Ereignissen.

user profile iconmandras hat folgendes geschrieben Zum zitierten Posting springen:
Wenn Du im OnClick einer Schaltfläche CLOSE aufrufst,
wird über Zwischenwege Application.Terminate aufgerufen, das sendet an Windows das PostQuitMessage, die Anwendung wird von Windows beendet.

Ebenfalls Danke. Das Problem ist, dass ich auch eine Idee suche (habe mich im 1.Beitrag nicht ganz konkret ausgedrückt), wenn nur ein Formular geschlossen werden soll und nicht die ganze Anwendung. Dann geht es so leider nicht.

Beste Grüße
Mathematiker

Nachtrag: Sehr ärgerlich. Die Idee mit

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
procedure TForm.WndProc(var msg:TMessage);
begin
    case Msg.Msg of
      WM_CLOSE: begin
                  if sofortabbruch=false then sofortabbruch:=true;
                  close;
                end  
    Else
      inherited;
    end;
end;

funktioniert in einer anderen Anwendung nicht. Dagegen bekomme ich die Meldung, dass mein WndProc die virtuelle Methode von TCustomform verbirgt. :nixweiss:


mandras - So 05.01.14 23:01

Hm..
1) Kann es sein, daß Du bei der Deklaration der WndProc das override vergessen hast?
2) Ich habe (allerdings mit D6) eine Anwendung mit 2 Fenstern erstellt.
Auf dem Hauptfenster ein Button, der nur das 2. Fenster anzeigt.
2. Fenster: Ein Button der die "repeat ... processmessages" auslöst.

Wenn ich "Close" im Fenstertitel klicke passiert folgendes:
Fenster 2 wird geschlossen, Fenster 1 läßt sich nicht mehr über sein "Close" im Titel schließen.
Soweit logisch, Fenster 2 ist unsichtbar, die Schleife mit dem Repeat aber noch aktiv.

! Bei dieser Konstellation kann ich aber in der Repeat-Schleife prüfen, ob das Form noch auf "visible" steht. -> Wenn nein: Schleife beenden.
! Wenn das Form mit der processmessages-Schleife nicht das Hauptformular der Anwendung ist, werden hier wie erwartet die OnClose-Ereignisse ausgelöst über die Close-Fläche im Titel.


Mathematiker - So 05.01.14 23:08

Hallo,
user profile iconmandras hat folgendes geschrieben Zum zitierten Posting springen:
1) Kann es sein, daß Du bei der Deklaration der WndProc das override vergessen hast?

Genau das ist es und wieder typisch für mich. :autsch:
Besten Dank, denn jetzt funktioniert es mit WndProc einwandfrei.

Beste Grüße
Mathematiker


Delphi-Laie - So 05.01.14 23:19

user profile iconmandras hat folgendes geschrieben Zum zitierten Posting springen:
Wenn ich "Close" im Fenstertitel klicke passiert folgendes:
Fenster 2 wird geschlossen, Fenster 1 läßt sich nicht mehr über sein "Close" im Titel schließen.
Soweit logisch, Fenster 2 ist unsichtbar, die Schleife mit dem Repeat aber noch aktiv.


Das ist tückisch und war mir zugegenermaßen auch nicht (mehr) bewußt. Bei Programmen, die mit dem Visual-Studio erstellt wurden, beendet der X-Druck das Formular tatsächlich, auch untergeordnete Formulare (zumindest die mit C# erstellten, soweit ich mich recht entsinne). Greift man danach - z.B. in guter alter Delphi-Gewohnheit, es wieder sichtbar zu machen - erneut darauf zu, kommt natürlich irgendein Programmfehler (Exzeption o.ä.).

Unabhängig vom Formular kann man natürlich die Variable in der repeat-until-Schleife ständig abfragen und natürlich ggf. die Schleife beenden.


Mathematiker - Mo 06.01.14 20:32

Hallo,
ich muss noch einmal auf das Thema zurückkommen. Durch den Hinweis von user profile iconmandras auf ein geschlossenes Fenster mit noch laufender Schleife bin ich vorsichtig geworden.
Daher habe ich Folgendes versucht:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
procedure form.animation(sender:tobject);
begin
    if animationlaeuft then animationlaeuft:=false
    else
    begin
       animationlaeuft:=true;
       schliessbar:=false;
       repeat
         ... application.processmessages; ...  
       until not animationlaeuft;
       schliessbar:=true;
    end;
end;
procedure form.WndProc(var msg:TMessage);
begin
   case Msg.Msg of
      WM_CLOSE: begin
                  if animationlaeuft then animationlaeuft:=false;
                  if schliessbar then close;
                end
    Else
      inherited;
    end;
end;

Erwartet habe ich nun, dass bei Klick auf das X (Schließen) die Animation gestoppt wird und das Fenster sich schließt. Und natürlich schließt es sich nicht!
D.h. also bei der ursprünglichen Variante

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
procedure form.WndProc(var msg:TMessage);
begin
   case Msg.Msg of
      WM_CLOSE: begin
                  if animationlaeuft then animationlaeuft:=false;
                  close;
                end
    Else
      inherited;
    end;
end;

schließt sich das Fenster und die Schleife läuft wohl noch weiter; oder wird geschlossen, wenn das Fenster nicht mehr existiert (geht ja wohl nicht), da ich bei der Rückkehr zum aufrufenden Formular form.release aufrufe.
Dann müsste es doch aber irgendwelche Probleme geben, von harmlosen Fehlermeldungen bis gravierenden Programmabstürzen.
Aber nichts passiert. Ich habe heute auf meinem "schnellen" PC unter Win 8.1 getestet, auf einem etwas langsameren mit Win 7 (64) und auf einer "lahmen Ente" mit XP.
In allen Fällen gab es keine Schwierigkeiten, wenn ich sofort das close ohne den Test auf schliessbar aufrufe. Mit dem Test bleibt das Fenster offen.

Das verstehe ich nun nicht mehr.
Kann ich davon ausgehenden, dass die Methode WndProc in der Form ohne den Sicherheitstest, ob die Animation beendet ist, funktioniert? Irgendwie werde ich das Gefühl nicht los, dass da noch ein "böser" Fehler lauert, der zuschlägt, wenn ich es nicht erwarte.

Beste Grüße
Mathematiker


Delete - Mo 06.01.14 22:37

Wieso nicht einfach in OnCloseQuery vor der Zeile CanClose := True die Variable animationlaeuft auf False setzen?


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
Procedure TFormMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  CanClose := False;

  AnimationLaeuft := False;  
  SonstigenKramErledigen;
  AlleKlassenFreigeben;

  CanClose := True;
end;


OnCloseQuery wird beim Schließen des Formulars aufgerufen. Ist das Forumlar das Hauptformular, wird das Programm beendet.


Mathematiker - Mo 06.01.14 22:50

Hallo,
Danke für die Antwort.
user profile iconPerlsau hat folgendes geschrieben Zum zitierten Posting springen:
Wieso nicht einfach in OnCloseQuery vor der Zeile CanClose := True die Variable animationlaeuft auf False setzen? ...
OnCloseQuery wird beim Schließen des Formulars aufgerufen. Ist das Forumlar das Hauptformular, wird das Programm beendet.

Das habe ich schon ausprobiert; und jetzt noch einmal. Und genau dort liegt mein Problem.
Während der Animation kann ich noch so oft auf den X-Button klicken. OnCloseQuery wird scheinbar nicht ausgelöst, denn das Programm springt einfach nicht in die Methode hinein; obwohl es funktionieren müsste.
Allerdings funktioniert es sofort, wenn es um das Hauptformular geht, nur eben nicht bei weiteren Formularen.

Ich weiß im Moment nicht weiter. Wahrscheinlich habe ich irgendwo ein anderes Problem.

Beste Grüße
Mathematiker


Delete - Mo 06.01.14 22:57

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Das habe ich schon ausprobiert; und jetzt noch einmal. Und genau dort liegt mein Problem. Während der Animation kann ich noch so oft auf den X-Button klicken. OnCloseQuery wird scheinbar nicht ausgelöst, denn das Programm springt einfach nicht in die Methode hinein; obwohl es funktionieren müsste. Ich weiß im Moment nicht weiter. Wahrscheinlich habe ich irgendwo ein anderes Problem.

Wenn du die Animation nicht als eigenen Thread laufen läßt, kommt dein "Programmzeiger" ja von der Animations-Procedure gar nicht mehr zurück, sobald diese gestartet wurde ... In der Message-Queue steht zwar schon der Klick auf das Titelzeilen-X und wartet darauf, endlich dranzukommen, aber er kann erst abgearbeitet, wenn die zuvor aufgerufene Procedure beendet ist.


Mathematiker - Mo 06.01.14 23:06

Hallo,
user profile iconPerlsau hat folgendes geschrieben Zum zitierten Posting springen:
Wenn du die Animation nicht als eigenen Thread laufen läßt, kommt dein "Programmzeiger" ja von der Animations-Procedure gar nicht mehr zurück, sobald diese gestartet wurde ...

Danke für den Hinweis.
Aber, deshalb wird ja in der Animation application.processmessages gerufen. Und wie weiter oben gesagt, über WndProc funktioniert es ja, aber mit dem für mich blöden Beigeschmack, dass ich der ganzen Sache nicht vertraue.

Ich vermute, dass es mit FormCloseQuery nicht so wie erwartet will, hat vielleicht etwas mit meinem nicht gerade frischem Delphi 5 und der Tatsache, dass das 2.Fenster modal ist, zu tun.

Beste Grüße
Mathematiker


mandras - Di 07.01.14 00:21

Ich habe (siehe oben) mit D6 getestet.
Meine Erkenntnisse waren:
Wenn das Form das MainForm ist klappt das ganze nicht (es wird zwar Application.Terminated gesetzt, da das Programm aber in der Repeat-Schleife "hängt" hat dies keine Auswirkungen. OnClose und OnCloseQuery wird nicht aufgerufen wenn man den "Close"-Button der Titelleiste betätigt. Über einen Extra-Button der die "Close"-Methode des Forms aufruft funktioniert es.

Wieder Close-Button der Titelleiste: Wenn das Form _nicht_ das Main Form ist wird es zwar geschlossen, die repeat-Schleife aber nicht beendet.

! Wenn das Form nicht Main Form ist, ist es egal, ob es modal ist oder nicht. Geschlossen wird es.

Der Code von Mathematiker von 19:32 ist auch nicht der Weisheit letzter Schluß:


Delphi-Quelltext
1:
2:
      if animationlaeuft then animationlaeuft:=false;
                  if schliessbar then close;


Das kann nicht funktionieren.
Schließbar ist vorher false. Es wird erst auf true gesetzt in .animation.
.animation wird aber erst NACH diesem Code aufgerufen. Daher wird Schließbar hier immer false sein, die Methode Close daher nicht aufgerufen.

Die ursprüngliche Variante ist m.E. nach zielführend. Das explizite Aufrufen von TForm.Close klappt immer. Da wird dann auch korrekt unterschieden, ob das Form nur geschlossen (nicht Main Form) oder das Programm beendet werden soll.


Delphi-Laie - Di 07.01.14 00:21

Allzu qualifiziert kann ich zu dieser Diskussion leider nichts beisteuern, aber hier:

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Ich vermute, dass es mit FormCloseQuery nicht so wie erwartet will, hat vielleicht etwas mit meinem nicht gerade frischem Delphi 5 und der Tatsache, dass das 2.Fenster modal ist, zu tun.


wage ich leichte Entwarnung zu geben: Schon Delphi 1, spätestens Delphi 2 hatten (ziemlich) ausgereifte Ereignisbehandlungen. Da wurde doch n.m.W. nichts mehr "korrigiert", es kamen eher / höchstens weitere Dinge hinzu.

Wie schon geschrieben, mein Sortierkino ist auch im laufenden Betrieb - dank application.processmessages - abbrechnenbar, und das kann ich in einer Version schon ab Delphi 2.0 compilieren.


Delphi-Laie - Di 07.01.14 00:24

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Während der Animation kann ich noch so oft auf den X-Button klicken. OnCloseQuery wird scheinbar nicht ausgelöst, denn das Programm springt einfach nicht in die Methode hinein; obwohl es funktionieren müsste.


Scheinbar oder tatsächlich?

Ich pflege mir immer mit einfachen Pieptönen in solchen Fällen zu helfen.

Wenn Du in OnClose und/oder OnCloseQuery einen Breakpoint setzt, wird dann ddas Program beim X-Druck an der Stelle unterbrochen oder nicht? Auch der Breakpoint kann von der IDE nicht ignoriert werden, wenn er angesprungen wird.


Mathematiker - Di 07.01.14 00:32

Hallo,
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Scheinbar oder tatsächlich? ... Wenn Du in OnClose und/oder OnCloseQuery einen Breakpoint setzt, wird dann ddas Program beim X-Druck an der Stelle unterbrochen oder nicht?

Tatsächlich!
Den Breakpoint habe ich gesetzt und nichts passiert.
Ich habe ein älteres Programm etwas verändert und hänge es einmal 'ran. Die Plasmadarstellung ist in einem modalen Fenster, so wie meine eigentliche Animation.
Startet man nach dem Zeichnen die Farbrotation hat der X-Button "keine Wirkung" mehr.

Ich danke allen für die Mühe und Hilfe und es eilt nicht.
Ich muss jetzt erst einmal ins Bett. Morgen früh 5.30 Uhr ist die Nacht leider vorbei.

Beste Grüße
Mathematiker


Delete - Di 07.01.14 12:17

Was mir als Erstes auffällt:

Du arbeitest so gerne mit Konstanten, die in Wirklichkeit KEINE sind.
Ständig weist Du den "Konstanten" andere Werte zu!


Delphi-Laie - Di 07.01.14 13:37

Das ist in der Tat merkwürdig.

Um das Problem zu vereinfachen (neudeutsch: "herunterzubrechen"), erstellte ich mal ein kleines Projekt, absichtlich mit Delphi 2. Es funktioniert so, wie es m.E. funktionieren sollte. Die beiden Pieptöne in aufsteigender Frequenz beim X-Druck sollen einfach das Durchlaufen von OnCloseQuery und OnClose (in dieser Reihenfolge) anzeigen.

Drei Sachen sind jedoch bemerkenswert:

1. Obwohl erst in OnCloseQuery (oder in OnClose, funktioniert auch dort) die Abbruchbedingung der Schleife "freigeschaltet" wird, die Schleife also mindestens noch einmal angesprungen und damit das Abbrechen unterbrochen wird, funktioniert dennoch auch das Schließen des (jeweiligen) Formulares oder gar das Beenden des Programmes. Das Schließen ist also ein wahrlich komplexer Prozeß, der unterbrech- und fortsetzbar ist, wobei OnClose(Query) jeweils nur einmal durchlaufen werden.

2. Das Hauptformular und damit das Programm können nicht beendet werden, wenn die Schleife im unter- bzw. zugeordneten Formular noch läuft. Eine laufende Schleife verhindert also das Programmende.

3. Wird nach 2. das untergeordnete Formular und mit ihm seine Schleife beendet, beendet sich das Programm ohne erneuten X-Druck auf das Hauptformular. Das Programm merkt sich demnach auf mysteriöse Weise, daß es schon einmal (erfolglos) zu beenden versucht wurde.

Derlei Tücken wie hier das Nichtanspringen der OnClose-Ereignisbehandlungsprozeduren sind mir in prinzipieller Form wohlbekannt. Es gibt für jemanden wie mich, der wenig Ahnung hat, zwei Möglichkeiten, der Ursache auf die Spur zu kommen (wobei ich die erstere wegen tendenziell geringerem Aufwand bevorzuge). Entweder das problembehaftete Programm soweit vereinfachen ("herunterbrechen"), daß es einen solch simplen Charakter bekommt wie meines (zwischendurch immer wieder die Löschschaktionen dokumentieren, um die Ursache einzukreisen) oder ein simples Programm immer mehr mit Leben erfüllen (hier sind ebenfalls die Dokumentation, was hinzugefügt wird, wichtig).

Postscriptum: Den Wert boolescher Variablen über den = -Operator abzufragen, ist unelegant. Statt =false besser if not ...


Delete - Di 07.01.14 13:50

Ich habe mal ein paar Änderungen gemacht:

- procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean); // entfällt ! da ohne Wirkung
- Button Exit1 eingefügt


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
var
  Form1: TForm1;
  zeichnenaktiv:boolean;// = false;
  rotation:boolean;// = false;
  waagerecht:boolean;// = false;
  stopp : Boolean;

implementation

{$R *.DFM}
(*
const rotation:boolean = false;
      waagerecht:boolean = false;
      zeichnenaktiv:boolean = false;
      abbruch:boolean = true;             *)



procedure TForm1.FormCreate(Sender: TObject);
begin
      zeichnenaktiv:= false;
      rotation:= false;
      waagerecht:= false;
      stopp:=false;
end;

procedure TForm1.Exit1Click(Sender: TObject);
begin
  stopp:=true;
  Form1.Close;
end;

procedure TForm1.Button2Click(Sender: TObject);
var i,j:integer;
    k,index  : integer;
begin
   if not rotation then
   begin
      //Initialisierung für Farbrotation
      cyclestart:=0;
      cb1.enabled:=false;
      button2.caption:='Stop';
      rotation:=true;
///Rotationsschleife
   repeat
   if stopp=true then exit;  //th

     cyclestart:=cyclestart+1;
...


WasWeißDennIch - Di 07.01.14 14:14

Lass doch bitte das "Form1" im Exit1Click weg. Und Vergleiche auf true müssen nicht immer das richtige Ergebnis liefern, daher "=true" im Button2Click auch mit streichen.


Delete - Di 07.01.14 14:24

Close oder Form1.Close
Was spielt das für eine Rolle?

Wenn Du schon was zu meckern hast, dann begründe es!


WasWeißDennIch - Di 07.01.14 14:37


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
var 
  Form1: TForm1;

procedure TForm1.MachZu;
  Form1.Close;
end;

Das funktioniert genau solange, wie eine Instanz von TForm1 in der globalen Variablen Form1 hinterlegt ist. Hier würde es knallen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
var 
  Form1: TForm1;
  FormMain: TForm1;

...

FormMain := TForm1.Create(Applicaton);

...

procedure TForm1.MachZu;
  Form1.Close;
end;

Hier gibt es keine Instanz von TForm1 in Form1, sondern in FormMain. Und das ist der entscheidende Unterschied. Und selbst wenn es beide Instanzen gibt, wird immer auf dieselbe zugegriffen, das ist mit ziemlicher Sicherheit so nicht beabsichtigt.


Mathematiker - Di 07.01.14 14:51

Hallo,
Danke für die Hinweise.
Einen Ausstiegsschalter in der beschriebenen Form hatte ich schon eingefügt und der funktionierte auch fehlerfrei.

Scheinbar gibt es nur die eine Möglichkeit, während einer nicht timergesteuerten Animation in einem modularen Fenster, den X-Button der Titelzeile aufzurufen (und darum geht es mir ja). Also habe ich bei jeder derartigen Animation wieder

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
procedure TForm.WndProc(var msg:TMessage);
begin
    case Msg.Msg of
      WM_CLOSE: begin
                  if not sofortabbruch then sofortabbruch:=true;
                  close;
                end  
    Else
      inherited;
    end;
end;

eingefügt und massiv getestet. FastMM zugeschaltet und immer wieder an den unmöglichsten Stellen während der Animation auf X gedrückt, ergab bei keiner der Routinen Probleme. Bisher jedenfalls nicht.
Mal sehen, ob in der nächsten Zeit auf irgendeinem Rechner doch Schwierigkeiten auftreten. Wenn ja, ist es ärgerlich, aber es gibt ja keine fehlerfreie Software und von mir(!) schon gar nicht. :wink:

Außerdem habe ich angefangen, einige Animationen doch mit einem Timer zu steuern. Es wird etwas langsamer, aber bei den heutigen Rechnern immer noch schnell genug.

Beste Grüße und Danke für die Hilfe
Mathematiker


Delphi-Laie - Di 07.01.14 14:52

Wird das Formular 1 genau wie Formular 2 erzeugt (in program pplasma):


Delphi-Quelltext
1:
2:
3:
4:
5:
  Application.Initialize;
  Application.Title := 'Plasma';
  Application.CreateForm(TForm2, Form2);
  Application.CreateForm(TForm1, Form1);
  Application.Run;


und wird das Formular 1 in unit plasmaaufrufen so aufgerufen:


Delphi-Quelltext
1:
2:
3:
4:
procedure TForm2.Button1Click(Sender: TObject);
begin
form1.show
end;


, dann läßt sich das Formular 1 auch einfach und sauber beenden (inkl. Schleifenabbruch). Heureka?!


Delphi-Laie - Di 07.01.14 14:59

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Außerdem habe ich angefangen, einige Animationen doch mit einem Timer zu steuern. Es wird etwas langsamer, aber bei den heutigen Rechnern immer noch schnell genug.


Zwar steht es mir nicht zu, Dich zu bevormunden, Mathematiker, aber wenn ich etwas dazu loslassen darf: Bitte nicht. Das ist unelegant. Gerade hohe Rechengeschwindigkeit begründet oft erst den eigentlichen Augenschmaus. Tendenziell, solange wir mit Computern zu tun hattenn, waren diese bisher immer doch eher zu langsam als zu schnell. Auch Deine Farbrotation wird umso beeindruckender, je schneller sie abläuft (natürlich nur bis zu einer gewissen Grenze). Insofern wäre es schade, ein wertvolles Potential zu verschenken.

Außerdem haben Timer gerade nicht(s) mit der Rechengeschwindigkeit zu tun, schon gar nicht bei "heutigen Rechnern", die "immer noch schnell genug" sind. Höchstens umgekehrt wird ein Schuh daraus: Wenn ein Computer zu langsam ist, kann es sein, daß der Timer nicht mehr hinterherkommt und auslösende Ereignisse ausgelassen werden ("unter den Tisch fallen").


jaenicke - Di 07.01.14 15:25

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Ich habe ein älteres Programm etwas verändert und hänge es einmal 'ran. Die Plasmadarstellung ist in einem modalen Fenster, so wie meine eigentliche Animation.
Startet man nach dem Zeichnen die Farbrotation hat der X-Button "keine Wirkung" mehr.
Lösen lässt sich das sehr einfach. Du musst die Abbruchbedingung der Schleife so schreiben:

Delphi-Quelltext
1:
    until not rotation or (ModalResult <> mrNone);                    
Hintergrund ist, dass du das aus einem ShowModal aufrufst und dementsprechend in einer neuen Nachrichtenwarteschlange.

Dein Assemblercode in der Funktion farbmitte ist jedenfalls nicht korrekt. Richtig (ohne genauer geschaut zu haben was der macht):

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
         cmp eax, 0         //Test auf Null
         jz @null
         jmp @end
         @null: mov eax, 1
         @end: nop

       end;
Hintergrund:
Das ret kommt beim end automatisch. Dadurch, dass du noch ein eigenes einfügst, zerschießt du dir den Stack, und zwar gründlich...


Delete - Di 07.01.14 15:47

Ich habe jetzt DIE Lösung, die auch unseren Mathematiker zufriedenstellen wird:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
...
private
     FCanClose : Boolean;
    procedure WMNCLBUTTONDOWN(var msg: TMessage); message WM_NCLBUTTONDOWN;

...
procedure TForm1.WMNCLBUTTONDOWN(var msg: TMessage);
begin
  if msg.wParam = HTCLOSE then
  BEGIN
  Windows.Beep(800,50);
  FCanClose:=true;
  Close;
  END;
  inherited//muss hier stehen, weil sonst der [X]-Button nicht funktioniert!
end;
...

///Rotationsschleife
   repeat
   if FCanClose = True then exit;
...


Mathematiker - Di 07.01.14 15:55

Hallo,
user profile iconhathor hat folgendes geschrieben Zum zitierten Posting springen:
Ich habe jetzt DIE Lösung, die auch unseren Mathematiker zufriedenstellen wird:

Danke, werde ich sofort ausprobieren.
Aber ...

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Lösen lässt sich das sehr einfach. Du musst die Abbruchbedingung der Schleife so schreiben:

Delphi-Quelltext
1:
    until not rotation or (ModalResult <> mrNone);                    
Hintergrund ist, dass du das aus einem ShowModal aufrufst und dementsprechend in einer neuen Nachrichtenwarteschlange.

"Sehr einfach"? Ja, es ist sehr einfach, aber auf so etwas wäre ich nie gekommen. Es funktioniert absolut perfekt.
Besten Dank und tiefe Verbeugung :flehan: .

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Dein Assemblercode in der Funktion farbmitte ist jedenfalls nicht korrekt. ...

Super. Bei einem Problem bekomme ich gleich die Lösungen für zwei Sachen.
Nochmals vielen Dank.

Beste Grüße
Mathematiker


Delphi-Laie - Di 07.01.14 15:56

Kann nicht dieser redundante und hinsichtlich binärer Interna genaugenommen sogar falsche (weil fehlerträchtige) Vergleich boolescher Variablen mit "true" bzw. "false" endlich mal aufhören?


WasWeißDennIch - Di 07.01.14 15:56

user profile iconhathor hat folgendes geschrieben Zum zitierten Posting springen:

Delphi-Quelltext
1:
2:
   if FCanClose = True then exit;
...

Ich hatte es zwar eben schon angesprochen, aber bitteschön: http://delphi-treff.de/tutorials/objectpascal/programmierung-mit-boolean-werten/typische-anfaengerfehler/ [http://delphi-treff.de/tutorials/objectpascal/programmierung-mit-boolean-werten/typische-anfaengerfehler/]


Mathematiker - Di 07.01.14 16:28

Hallo,
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Kann nicht dieser redundante und hinsichtlich binärer Interna genaugenommen sogar falsche (weil fehlerträchtige) Vergleich boolescher Variablen mit "true" bzw. "false" endlich mal aufhören?

Ich muss user profile iconhathor in Schutz nehmen.
Er hat nur das gemacht, was ich schon begonnen hatte. Der Vergleich auf "true" usw. stammt aus meinem Beispielprogramm. :autsch:

Beste Grüße
Mathematiker


WasWeißDennIch - Di 07.01.14 16:32

Dann schäme er sich, und ihm wird vergeben werden :zwinker:


Delphi-Laie - Di 07.01.14 17:35

Nunja, ich bin mir sicher, daß das Helferlein es besser wußte und nehme zu seinem Gunsten an, daß nur Flüchtigkeit die Ursache war. Allerdings wurde das in dieser Diskussion auch schon vorher moniert.

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Der Vergleich auf "true" usw. stammt aus meinem Beispielprogramm. :autsch:


Vermutlich aber letztlich aus Deinem übergeordneten Programm. Programmierstile schleifen sich über Jahre ein und verfestigen sich, man schleppt einiges ewig mit sich umher - sei es mental, sei es in Form so mancher Quelltexte.

Auch die In-/Dekrementierung integrer Variablen geht übrigens eleganter als mit den Rechenoperationen. Allerdings soll der Compiler das ohnehin erkennen und optimieren.

Warum Dein (scheinbar unbeendbares) Fenster, das uns so sehr beschäftigte, modal sein soll, erschließt sich mir nicht. Klar, es wird erst zur Laufzeit (besser: bei Bedarf) erzeugt, aber welchen Vorteil hat das? Speicherbedarfsreduktion? Konsequenterweise sollte es dann beim X-Druck auch zur Laufzeit "zerstört" werden.


jaenicke - Di 07.01.14 18:21

user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Warum Dein (scheinbar unbeendbares) Fenster, das uns so sehr beschäftigte, modal sein soll, erschließt sich mir nicht. Klar, es wird erst zur Laufzeit (besser: bei Bedarf) erzeugt, aber welchen Vorteil hat das?
Schnellerer Start? Bei vielen Formularen dauert der Start sonst ewig, vom Speicherverbrauch ganz abgesehen.
Und modal, damit man an das aufrufende Fenster derweil nicht herankommt, dafür ist modal ja da.


Delete - Di 07.01.14 18:22

user profile iconWasWeißDennIch hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconhathor hat folgendes geschrieben Zum zitierten Posting springen:
...

Ich hatte es zwar eben schon angesprochen, aber bitteschön: [url=http://delphi-treff.de/tutorials/objectpascal/programmierung-mit-boolean-werten/typische-anfaengerfehler/]http://delphi-treff.de/tutorials/objectpascal/programmierung-mit-boolean-werten/typische-anfaengerfehler/[/url]


Wer sich ausgerechnet auf diese Seite bezieht, sollte mal das Gehirn einschalten!


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
function GaaanzFieseAPIFunktion: Boolean;
asm
  mov eax, -$01
end;

//Diese Function ist völlig ungeeignet, um die Boolean-Problematik darzustellen:

BoolToStr(GaaanzFieseAPIFunktion)); 
// ergibt eine Farbe: clLime, aber kein boolsches Äquivalent.
//Manchmal ist es auch rot:
Form1.Color:= Integer(GaaanzFieseAPIFunktion);


var
  b := Boolean;  //geht auch nicht! Was hat der Depp für ein DELPHI?


WasWeißDennIch - Di 07.01.14 18:32

Du weißt schon, dass Niveau keine Handcreme ist?