У меня небольшая проблема с ожидаемым объектом таймера, когда я использую абсолютное время и 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.