Dear Delphi programmers,
I'm looking for help how to write a one-shot timer (No GUI, so VCL Timers out of question)...
Let me explain a little bit more.
In my code (explaining with VCL timer but in this particular project I have no forms):
procedure
which send a char over serial portInterval
In the OnTimer
event:
I have a code which send a char then disable the timer itself to never be executed again.
The problem is that I need to make the creation of these timers dynamic.
I thought of the function SetTimer()
then KillTimer()
in the "OnTimer event" to disable it (free it).
Is it a good (safe) way?
Thank you!
A shot timer is a shot activated timer used in shooting sports, which starts the competitor by an audible signal and also records the competitor's time electronically by detecting the sound of each shot together with the time from the start signal.
Off-delay timersAn output is energized by applying the trigger, which must be removed for the time delay to start. An output is de-energized at the end of the time delay period. If the trigger is applied during the delay, it will reset.
When the Timer Counter Register reaches zero and auto-reload mode is enabled, it reloads the value in the Timer Load Register and then decrements from that value. If auto-reload mode is not enabled, the Timer Counter Register decrements down to zero and stops.
A Timer Relay is a combination of an electromechanical output relay and a control circuit. The contacts will open or close before or after a pre-selected, timed interval.
Is it safe to kill timer from inside of a timer event ?
Yes, that's perfectly safe.
How to implement simplest one shot timer ?
The easiest implementation of a 1 second one shot timer is this, but note, that if you start more of them, you won't be able to distinguish which one of them elapsed its interval:
procedure TimerProc(hwnd: HWND; uMsg: UINT; idEvent: UINT_PTR;
dwTime: DWORD); stdcall;
begin
KillTimer(0, idEvent);
ShowMessage('I''m done!');
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
SetTimer(0, 0, 1000, @TimerProc);
end;
The multimedia timer API provides support for a one shot timer. The benefit is, that the timing is much more precise than the SetTimer/KillTimer solution and you can use it with intervals <50 ms. This comes at a price, as the callback does not return in the context of the main thread. Here is my implementation of a one-shot timer using the multimedia timer API:
unit MMTimer;
interface
uses windows, Classes, mmsystem, SysUtils;
TOneShotCallbackEvent = procedure (const UserData: Pointer) of object;
(*
The MMOneShotCallback function calls the Callback after the Interval passed.
** Attention: **
The Callback is not called within the context of the main thread.
*)
type TMMOneShotTimer = class(TObject)
private
FTimeCaps: TTimeCaps;
FResult: Integer;
FResolution: Cardinal;
public
constructor Create;
function MMOneShotCallback(const Interval: Cardinal; UserData: Pointer; Callback: TOneShotCallbackEvent): Boolean;
property Result: Integer read FResult;
property Resolution: Cardinal read FResolution;
end;
implementation
type
TOneShotCallbackData = record
Callback: TOneShotCallbackEvent;
UserData: Pointer;
end;
POneShotCallbackData = ^TOneShotCallbackData;
procedure OneShotCallback(TimerID, Msg: UINT;
dwUser, dw1, dw2: DWord); pascal;
var pdata: POneShotCallbackData;
begin
pdata := Pointer(dwUser);
pdata.Callback(pdata.UserData);
FreeMemory(pdata);
end;
constructor TMMOneShotTimer.Create;
begin
FResult := timeGetDevCaps(@FTimeCaps, SizeOF(FTimeCaps));
Assert(FResult=TIMERR_NOERROR, 'Call to timeGetDevCaps failed');
FResolution := FTimeCaps.wPeriodMin;
FResult := timeBeginPeriod(FResolution);
Assert(FResult=TIMERR_NOERROR, 'Call to timeBeginPeriod failed');
end;
function TMMOneShotTimer.MMOneShotCallback(const Interval: Cardinal; UserData: Pointer; Callback: TOneShotCallbackEvent): Boolean;
var pdata: POneShotCallbackData;
begin
GetMem(pdata, SizeOf(TOneShotCallbackData));
pdata.Callback := Callback;
pdata.UserData := UserData;
result := (0 <> timeSetEvent(Interval, FResolution, @OneShotCallback, DWord(pdata), TIME_ONESHOT));
if not result then
FreeMemory(pdata);
end;
end.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With