сброс сигнального дескриптора ожидаемого объекта таймера - PullRequest
1 голос
/ 04 июня 2019

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

Как только таймер получает сигнал, мне нужно сбросить объект, и единственный способ, который я могу найти, - это снова использовать setwaitabletimer с нереалистичным временем выполнения в будущем. Это будет держать таймер активным в ОС waketimers без всякой причины. Мне нужно сохранить дескриптор, потому что в некоторых ситуациях мне нужно повторно активировать таймер, и я использую объект таймера в массиве waitformultiplejectobjects, поэтому закрытие дескриптора FWaitTimer не будет хорошим.

Я правильно понимаю, или есть лучший способ сделать это?

Мой полный код выглядит следующим образом, не обращайте внимания на другие объекты waitfor, это всего лишь объект FWaitTimer.

unit frmMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.WinXPickers, cxGraphics, cxControls, cxLookAndFeels, cxLookAndFeelPainters, dxSkinsCore,
  dxSkinTheAsphaltWorld, dxSkinsDefaultPainters, cxCalendar, Vcl.ExtCtrls, cxContainer, cxEdit, dxCore, cxDateUtils, cxDropDownEdit, cxTextEdit, cxMaskEdit,
  cxSpinEdit, cxTimeEdit, Vcl.Menus, Vcl.StdCtrls, cxButtons, dateutils, syncObjs, UTimer;

const
  WM_WATINGDONE = WM_USER + 100;

type

  TMyWaitThread = class(TThread)
  private
    FWaitTimer : THandle;
    FTerminateEvent : TEvent;
    FPeriodicTimer : TADTimer;
    FWaitTime: TDateTime;
    procedure SetWaitTime(const Value: TDateTime);
    property WaitTime : TDateTime read FWaitTime write SetWaitTime;
  public
    constructor Create; overload;
    constructor Create(CreateSuspended: Boolean); overload;
    destructor Destroy; override;
    procedure execute; override;
    procedure Terminate;
  end;



  TForm1 = class(TForm)
    cxClock1: TcxClock;
    Timer1: TTimer;
    cxDateEdit1: TcxDateEdit;
    btnWait: TcxButton;
    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btnWaitClick(Sender: TObject);
  private
    FMyWaitThread : TMyWaitThread;
    Fwaiting: Boolean;
    procedure WM_WAITINGDONE(var msg : TMessage); message WM_WATINGDONE;
    procedure Setwaiting(const Value: Boolean);
    { Private declarations }
    property waiting : Boolean read Fwaiting write Setwaiting;
    procedure WatingDone;


  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnWaitClick(Sender: TObject);
var
  dt : TDateTime;
begin
  dt := cxDateEdit1.EditValue;
  FMyWaitThread.SetWaitTime(dt);
  waiting := True;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  cxClock1.Time := now;
  Timer1.Enabled := True;
  waiting := False;

  FMyWaitThread := TMyWaitThread.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FMyWaitThread.Terminate;
  FreeAndNil(FMyWaitThread);
end;

procedure TForm1.Setwaiting(const Value: Boolean);
begin
  Fwaiting := Value;
  case Fwaiting of
    True : begin
      btnWait.Enabled := False;
    end;

    false : begin
      btnWait.Enabled := True;
      cxDateEdit1.EditValue := IncMinute(Now, 1);
    end;
  end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  cxClock1.Time := now;
end;

procedure TForm1.WatingDone;
begin
  waiting := False;
end;

procedure TForm1.WM_WAITINGDONE(var msg: TMessage);
begin
  waiting := False;
end;

{ TMyWaitThread }

function GetGUID : string;
var
  uid : TGUID;
  r : Integer;
begin
  Result := '';
  r := CreateGuid(Uid);
  if r = S_OK then
  begin
    Result := StringReplace(GuidToString(Uid),'{', '', [rfReplaceAll]);
    Result := StringReplace(result, '}', '', [rfReplaceAll]);
  end;
end;

constructor TMyWaitThread.Create(CreateSuspended: Boolean);
begin
  inherited Create(CreateSuspended);
  FWaitTimer := CreateWaitableTimer(nil, True, PWideChar(GetGUID)); // change the 'MatrixTimer' to something unique...
  FTerminateEvent := TEvent.Create;
  FPeriodicTimer := TADTimer.Create;
  FPeriodicTimer.Interval := 10000;
end;

destructor TMyWaitThread.Destroy;
begin
  FPeriodicTimer.StopTimer;
  FreeAndNil(FPeriodicTimer);
  FreeAndNil(FTerminateEvent);
  CloseHandle(FWaitTimer);
  inherited;
end;

procedure TMyWaitThread.execute;
var
  EventArr : array of THandle;
begin
  SetLength(EventArr, 3);

  EventArr[0] := FTerminateEvent.Handle;
  EventArr[1] := FWaitTimer;
  EventArr[2] := FPeriodicTimer.Handle;

  while not Terminated do
  begin
    try
      case WaitForMultipleObjects(Length(EventArr), @EventArr[0], False, INFINITE) of

        WAIT_OBJECT_0 : begin // terminate
          OutputDebugString('Terminating.....');
          Exit;
        end;

        WAIT_OBJECT_0+1 : begin // wait timer
          OutputDebugString('Wait timer was triggered');

          WaitTime := IncMinute(now, 1);

          // How do I reset the FWaitTimer - I would like to keep the FWaitTimer handle so closing it is no good.

          PostMessage(Form1.Handle, WM_WATINGDONE, 0, 0);
        end;

        WAIT_OBJECT_0+2 : begin // periodic timer
          OutputDebugString('Periodic timer was triggered');
        end;

      end;

    except on E: Exception do
      // keep any exceptions inside the loop
    end;
  end;
end;

procedure TMyWaitThread.SetWaitTime(const Value: TDateTime);
var
  WakeUpTime: LARGE_INTEGER;
  SysTime : _SystemTime;
  FTime : _FileTime;
begin
  FWaitTime := Value;
  DateTimeToSystemTime(FWaitTime, SysTime);
  SystemTimeToFileTime(SysTime, FTime);
  LocalFileTimeToFileTime(FTime, FTime);
  WakeUpTime.LowPart := FTime.dwLowDateTime;
  WakeUpTime.HighPart := FTime.dwHighDateTime;
  SetWaitableTimer(FWaitTimer, WakeUpTime.quadpart, 0, nil, nil, True);
end;

procedure TMyWaitThread.Terminate;
begin
  FTerminateEvent.SetEvent;
  inherited;
end;

constructor TMyWaitThread.Create;
begin
  Create(False);
end;

end.

1 Ответ

3 голосов
/ 04 июня 2019

Цитата из документации (выделено мной):

Поток вызывает функцию SetWaitableTimer для активации таймера.Обратите внимание на использование следующих параметров для SetWaitableTimer: Используйте параметр lpDueTime, чтобы указать время, в которое таймер должен быть установлен в сигнальное состояние. Когда таймер ручного сброса установлен в сигнальное состояние, он остается в этом состоянии до тех пор, пока SetWaitableTimer не установит новое время выполнения. Когда таймер синхронизации установлен в сигнальное состояние, он остается в этом состоянии до тех пор, покапоток завершает операцию ожидания для объекта таймера.

Таким образом, единственный способ - снова вызвать SetWaitableTimer для сброса сигнального состояния таймера.

Существует 2 способарешите вашу проблему, используйте текущий хак, но добавьте CancelWaitableTimer() после SetWaitableTimer() для отмены таймера (его состояние останется без сигнала)

Лучшее решение - не ждать таймера, когда он не используетсяРеализация как @RbMm предлагает:

поместить FWaitTimer на 2 (последнее) место в массиве - EventArr [2]: = FWaitTimer и передать 3 или 2 в качестве первого аргумента WaitForMultipleObjects

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...