简体   繁体   中英

Delphi TTimer providing unusual results in Win 10

I have an app that allows my users to turn on and off a timer to track their time spent on a certain task. The timer runs a clock used to show the elapsed time to the user, much like a stopwatch.

The code below has worked as I thought it should for a few years now. However, when the app is run on Win 10, sometimes the "time" rate speeds up by 2 or 3 times during a session. If the user restarts the app, it may run at normal speed.

Win 10 Delphi 10.3

procedure TfmTimeCard.btnTimerClick(Sender: TObject);
begin
  if btnTimer.Caption = 'Start &Timer' then
  begin
    btnTimer.Down := True;
    btnTimer.Caption := 'Stop &Timer';
    pnlTimer.Color := clPurple;
    btnResume.Enabled := True;
    btnAssign.Enabled := False;
    Timer1.Enabled := true;
    UpdateTimer.Enabled := True;
    ElapsedTime := ElapsedTime;
    //btnPostRecord.Enabled := False;
    btnCancel.Enabled := False;
    btnDeleteTimeCard.Enabled := False;
  end
  else
  begin
    btnTimer.Down := False;
    btnTimer.Caption := 'Start &Timer';
    pnlTimer.ParentColor := True;
    btnResume.Enabled := False;
    btnAssign.Enabled := True;
    pnlTimer.Color := clMoneyGreen;
  end;
end;

procedure TfmTimeCard.Timer1Timer(Sender: TObject);
begin
  if btnTimer.Caption = 'Stop &Timer' then
  begin
    ElapsedTime := ElapsedTime + 0.0000115740;
    cxClock1.time := ElapsedTime;
    cxTimeEditTimer.Time := ElapsedTime;
  end;
end;

This is a terrible way to keep track of elapsed time with a TTimer . TTimer is not a real-time timer, or even an accurate timer. It is based on the WM_TIMER window message, which is

a low-priority message. The GetMessage and PeekMessage functions post this message only when no other higher-priority messages are in the thread's message queue .

Don't calculate your ElapsedTime based on how often the TTimer fires its OnTimer event. Keep track of the current time when starting the TTimer , and then subtract that value from the next current time whenever the OnTimer event is eventually generated. That will give you a more real elapsed time.

Try something more like this:

uses
  ..., System.DateUtils;

private
  StartTime: TDateTime;
  ElapsedSecs: Int64;

procedure TfmTimeCard.btnTimerClick(Sender: TObject);
begin
  if btnTimer.Tag = 0 then
  begin
    btnTimer.Tag := 1;
    ...
    ElapsedSecs := 0;
    StartTime := Now;
    Timer1.Enabled := true;
    ...
  end
  else
  begin
    btnTimer.Tag := 0;
    ...
    ElapsedSecs := SecondsBetween(Now, StartTime);
    Timer1.Enabled := false;
    ...
  end;
end;

procedure TfmTimeCard.Timer1Timer(Sender: TObject);
begin
  if btnTimer.Tag = 1 then
  begin
    ElapsedSecs := SecondsBetween(Now, StartTime);
    // use ElapsedSecs as needed ...
  end;
end;

Or:

uses
  ..., Winapi.Windows;

private
  StartTime: DWORD;
  ElapsedSecs: Integer;

procedure TfmTimeCard.btnTimerClick(Sender: TObject);
begin
  if btnTimer.Tag = 0 then
  begin
    btnTimer.Tag := 1;
    ...
    ElapsedSecs := 0;
    StartTime := GetTickCount;
    Timer1.Enabled := true;
    ...
  end
  else
  begin
    btnTimer.Tag := 0;
    ...
    ElapsedSecs := (GetTickCount - StartTime) div 1000;
    Timer1.Enabled := false;
    ...
  end;
end;

procedure TfmTimeCard.Timer1Timer(Sender: TObject);
begin
  if btnTimer.Tag = 1 then
  begin
    ElapsedSecs := (GetTickCount - StartTime) div 1000;
    // use ElapsedSecs as needed ...
  end;
end;

Or:

uses
  ..., System.Diagnostics;

private
  SW: TStopwatch;
  ElapsedSecs: Integer;

procedure TfmTimeCard.btnTimerClick(Sender: TObject);
begin
  if not SW.IsRunning then
  begin
    ...
    ElapsedSecs := 0;
    SW := TStopWatch.Start;
    Timer1.Enabled := true;
    ...
  end
  else
  begin
    ...
    SW.Stop;
    ElapsedSecs := Trunc(SW.Elapsed.TotalSeconds);
    Timer1.Enabled := false;
    ...
  end;
end;

procedure TfmTimeCard.Timer1Timer(Sender: TObject);
begin
  if SW.IsRunning then
  begin
    ElapsedSecs := Trunc(SW.Elapsed.TotalSeconds);
    // use ElapsedSecs as needed ...
  end;
end;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM