Delphi TTimer дает необычные результаты в Win 10 - PullRequest
0 голосов
/ 01 марта 2019

У меня есть приложение, которое позволяет моим пользователям включать и выключать таймер для отслеживания своего времени, потраченного на выполнение определенной задачи.Таймер запускает часы, используемые для отображения истекшего времени пользователю, очень похоже на секундомер.

Приведенный ниже код работал так, как я думал, уже несколько лет.Однако, когда приложение запускается на Win 10, иногда «время» увеличивается во время сеанса в 2–3 раза.Если пользователь перезапустит приложение, оно может работать с нормальной скоростью.

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;

1 Ответ

0 голосов
/ 02 марта 2019

Это ужасный способ отслеживать истекшее время с TTimer.TTimer не является таймером реального времени или даже точным таймером.Он основан на оконном сообщении WM_TIMER, которое является

сообщением с низким приоритетом.Функции GetMessage и PeekMessage отправляют это сообщение только в том случае, если в очереди сообщений потока нет других сообщений с более высоким приоритетом .

Не вычисляйте ElapsedTimeв зависимости от того, как часто TTimer запускает событие OnTimer.Следите за текущим временем при запуске TTimer, а затем вычитайте это значение из следующего текущего времени, когда событие OnTimer является сгенерированным ,Это даст вам более реальное истекшее время.

Попробуйте что-то вроде этого:

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;

Или:

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;

Или:

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;
...