expertenaustausch > comp.lang.* > comp.lang.delphi.misc

Ole Jansen (24.06.2008, 15:34)
Moin,

Wenn ich ein Formular mit einem Timer (Delphi6, VCL) mit der
Maus an der Titelleiste anfasse und _nicht_ bewege, bleibt
der Timer (und andere Sachen auch) stehen, bis ich loslasse oder
anfange das Formular zu verschieben.
(Hab noch nicht ganz verstanden, warum eigentlich?)

Wenn ich die Methode wm_NCHitTest mit Unsinn überschreibe
kommt die Anwendung nicht aus dem Tritt, aber das Formular
läßt sich nicht mehr verschieben, solange der Timer läuft,
der Anwender ist verwirrt:

- Unter Private diese Methode definieren:

procedure WMNCHitTest(var M: TWMNCHitTest); message wm_NCHitTest;

- In Implementation den Code der Methode definieren:

procedure TForm1.WMNCHitTest (var M: TWMNCHitTest);
begin
inherited;
if Timer1.Enabled then
begin
//Ja, das ist Unfug!
if M.Result = htCaption then M.Result := htNowhere;
end;
end;

Hat jemand einen Tip, wie ich es besser machen kann?

Viele Grüße,

O.J.
Arno Garrels (24.06.2008, 16:08)
Ole Jansen wrote:
> Moin,
> Wenn ich ein Formular mit einem Timer (Delphi6, VCL) mit der
> Maus an der Titelleiste anfasse und _nicht_ bewege, bleibt
> der Timer (und andere Sachen auch) stehen, bis ich loslasse oder
> anfange das Formular zu verschieben.
> (Hab noch nicht ganz verstanden, warum eigentlich?)


Weil dann keine Messages mehr verarbeitet werden. TTimer erzeugt
ein versteckes Fenster, das die WM_TIMER Botschaften eines
Windows Timers empfängt.

> Wenn ich die Methode wm_NCHitTest mit Unsinn überschreibe
> kommt die Anwendung nicht aus dem Tritt, aber das Formular
> läßt sich nicht mehr verschieben, solange der Timer läuft,
> der Anwender ist verwirrt:
> Hat jemand einen Tip, wie ich es besser machen kann?


Multi-Threading? Aber ein Timer kann immer aus dem Tritt kommen.
Jede längere Schleife im selben Thread, während der keine Messages
verarbeitet werden, andere Prozesse, die mit 90% CPU-Last werkeln
etc...
Ole Jansen (24.06.2008, 17:04)
Moin Arno,

Danke für den Tip.

Arno Garrels schrieb:

> Weil dann keine Messages mehr verarbeitet werden. TTimer erzeugt
> ein versteckes Fenster, das die WM_TIMER Botschaften eines
> Windows Timers empfängt.


So was in der Art hatte ich mit auch gedacht.

>> Wenn ich die Methode wm_NCHitTest mit Unsinn überschreibe
>> kommt die Anwendung nicht aus dem Tritt,


Wenn ich die Fehler in meiner Anwendung logge werden _vor_ der
Verarbeitung von wm_NCHitTest für eine gewisse Zeit (ca. 0.5s) keine
Messages verarbeitet. Weiß jemand, was davor kommt?

>> aber das Formular
>> läßt sich nicht mehr verschieben, solange der Timer läuft,
>> der Anwender ist verwirrt:
>> Hat jemand einen Tip, wie ich es besser machen kann?


Ein Workarround wäre, wenn ich eine Flag setzen könnte und
mein Timer weiß, daß vorher das Fenster "festgehalten" wurde
und sich dann mit der Systemzeit neu synchronisiert.
Leider habe ich kein passendes Ereignis gefunden: OnPaint,
OnCanResize usw. kommen alle nach dem Timer, wenn vorher
"Stau" war.

> Multi-Threading?


Könnte es eventuell schon helfen, wenn ich für das Programm ein
unsichtbares Hauptformular erzeuge, auf dem der Timer ist und
nur alle visuellen Komponenten auf eine extra Form umziehe?

Aber ein Timer kann immer aus dem Tritt kommen.
> Jede längere Schleife im selben Thread, während der keine Messages
> verarbeitet werden, andere Prozesse, die mit 90% CPU-Last werkeln
> etc...


Alles nicht so kritisch. Ich will nur alle 200ms was aktualisiert
haben.

Viele Grüße,

O.J.
Arno Garrels (24.06.2008, 17:35)
Ole Jansen wrote:
> Moin Arno,
> Danke für den Tip.
> Arno Garrels schrieb:
> So was in der Art hatte ich mit auch gedacht.


Korrektur, das passiert nur, wenn man auf eines der Border Icons klickt,
Also Minimize-Icon etc., Titelleiste anfassen ist kein Problem (W2K und XP).
Ole Jansen (24.06.2008, 18:16)
Moin,

Arno Garrels schrieb:

> Korrektur, das passiert nur, wenn man auf eines der Border Icons klickt,
> Also Minimize-Icon etc., Titelleiste anfassen ist kein Problem (W2K und XP).


Aber warum hört dann der Button von meinem Beispielcode auf
zu blinken, während ich die Titelleiste anfasse und die Maus nicht
bewege und warum blinkt er weiter, sobald ich das Fenster verschiebe?

Falls Du da noch einen Tip hast...

Nix für ungut,

Ole
Arno Garrels (24.06.2008, 18:29)
Ole Jansen wrote:
>> Korrektur, das passiert nur, wenn man auf eines der Border Icons
>> klickt, Also Minimize-Icon etc., Titelleiste anfassen ist kein
>> Problem (W2K und XP).

> Aber warum hört dann der Button von meinem Beispielcode auf
> zu blinken, während ich die Titelleiste anfasse und die Maus nicht
> bewege und warum blinkt er weiter, sobald ich das Fenster verschiebe?


?, kann ich hier mit D7, W2K und XP (Themed und Klassik) nicht
nachvollziehen.

> Falls Du da noch einen Tip hast...


Hab ich leider nicht. Oder sollte das wirklich an D6 liegen?
Eher unwahrscheinlich, D6 ist die einzige Version seit D3, die ich
nicht kenne :(
Arno Garrels (24.06.2008, 18:49)
Arno Garrels wrote:
>> Falls Du da noch einen Tip hast...

> Hab ich leider nicht. Oder sollte das wirklich an D6 liegen?
> Eher unwahrscheinlich, D6 ist die einzige Version seit D3, die ich
> nicht kenne :(


Hab mal deine Demo als D7 EXE hochgeladen:
Ole Jansen (25.06.2008, 08:47)
Moin Arno,
> Hab mal deine Demo als D7 EXE hochgeladen:
>


Ich hab mir das auch mal in Delphi7 gebaut.

Wenn ich das Timer Interval auf 100ms reduziere
und dann das Fenster an der Titelleiste anfasse
sehe ich deutlich, daß der Blinker aus dem Takt kommt.

Das gleiche Verhalten habe ich in Delphi6, wenn ich den Timer
auf ein anderes Formular setze als den Button :-(

Langsam bin ich ratlos. Windows ist ja kein Echtzeitbetriebssystem,
aber ein Heartbeat von ungefähr 100ms auf einem 2GHz Rechner muß doch
inzwischen auch als Einzelanwendung mit visuellen Komponenten
irgendwie hinzubekommen sein, ohne gleich mit Threads oder
Client/Server anzufangen?

Ole
Arno Garrels (25.06.2008, 12:47)
Ole Jansen wrote:

> aber ein Heartbeat von ungefähr 100ms auf einem 2GHz Rechner muß doch
> inzwischen auch als Einzelanwendung mit visuellen Komponenten
> irgendwie hinzubekommen sein,


WM_TIMER Messages, werden in die Botschaftenwarteschlange eines Threads
gestellt. Wenn vor der WM_TIMER Botschaft andere Messages warten oder
jemand mit SendMessage() direkt die WndProc aufruft oder gerade keine
Messages verarbeitet werden etc..... wird's halt ungenau.

Test:
-----

private
procedure WmUser(var Msg: TMessage); message WM_USER;
[..]
procedure TForm1.WmUser(var Msg: TMessage);
begin
Sleep(10);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
I : Integer;
begin
Timer1.Enabled := True;
for I := 1 to 200 do
Postmessage(Handle, WM_USER, 0, 0);
end;
-----

Bei einem Interval von 1000 würde hierbei das erste Timer-Ereignis
erst nach ca. 2000ms ausgelöst werden, denn zunächst müssen mindestens
200 andere Messages verarbeitet werden.

> ohne gleich mit Threads oder Client/Server anzufangen?


Selbst zusätzliche Threads werden dir keinen genauen Herzschlag
bescheren, auch wenn man natürlich viele, blockierende Aufgaben in
Threads auslagern kann und damit für eine flüssigere Message-
Verarbeitung im GUI-Thread sorgen kann.
Ähnliche Themen