【发布时间】:2019-06-05 03:47:10
【问题描述】:
当我使用绝对时间和 waitformultipleobjects () 时,我遇到了一个可等待计时器对象的小问题。
一旦收到定时器信号,我需要重置对象,我能找到的唯一方法是再次使用 setwaitabletimer 并在未来不切实际的到期时间。这将使定时器在操作系统唤醒定时器中毫无理由地保持活动状态。 我需要保留句柄,因为在某些情况下我需要重新激活计时器,并且我使用 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.
【问题讨论】:
-
您需要在数组中动态更改对象,等待。在发出计时器信号后 - 从传递给
WaitForMultipleObjects或MsgWaitForMultipleObjectsEx的数组中删除它。当它再次需要时 - 再次将其包含到数组中 -
在您的情况下,最简单的方法是将
FWaitTimer放在数组中的 2(最后)位置 -EventArr[2] := FWaitTimer并将 3 或 2 作为第一个参数传递给您的代码中的WaitForMultipleObjects。非常简单和清洁解决方案