重置信号通知的等待计时器对象句柄

时间:2019-06-04 09:38:57

标签: delphi winapi

当我使用绝对时间和waitformultipleobjects()时,我有一个等待计时器对象的小问题。

一旦收到计时器信号,我就需要重置对象,而我唯一能找到的方法是再次使用setwaitabletimer,并且将来的到期时间不切实际。这将毫无理由地使计时器在OS唤醒计时器中保持活动状态。 我需要保留该句柄,因为在某些情况下需要重新激活计时器,并且我在waitformultipleobjects数组中使用了计时器对象,因此关闭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 个答案:

答案 0 :(得分:3)

documentation(重点是我的话)中的语录:

  

该线程调用SetWaitableTimer函数来激活计时器。   请注意SetWaitableTimer使用以下参数:   lpDueTime参数指定计时器的时间   设置为信号状态。 将手动重置计时器设置为   信号状态,它将一直保持此状态,直到SetWaitableTimer   建立新的到期时间。。当同步计时器设置为   信号状态,直到线程完成   等待计时器对象上的操作。

所以唯一的方法是再次调用SetWaitableTimer以重置计时器的信号状态。

有2种方法可以解决您的问题,请使用当前的技巧,但在CancelWaitableTimer()之后添加SetWaitableTimer()以取消计时器(其状态将保持无信号)

更好的解决方案是不使用计时器时不等待计时器, 按照@RbMm的建议实施:

  

将FWaitTimer放置在数组的最后2个位置-EventArr [2]:=   FWaitTimer并将3或2作为第一个参数传递给WaitForMultipleObjects