I have a small problem with a waitable timer object when I use with absolute time and waitformultipleobjects ().
Once the timer gets signaled I need to reset the object, and the only way I can find is by using setwaitabletimer again with a unrealistic due time in the future. That'll keep the timer active in the OS waketimers for no reason at all. I need to keep the handle because I need to reactivate the timer in some situations and I use the timer object in the waitformultipleobjects array, so closing the FWaitTimer handle will not be good.
Do I understand this correct or is there a better way to do this?
My full code looks like this, please disregard the other waitfor objects, it's only the FWaitTimer object.
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.