I suggest you investigate Waitable timers. These can be set to set to fire after a specific period of time (like a regular TTimer) or at a specified time of day, which is exactly what you need in this case.
In your form create/show event, create a waitable timer and set it to the required time that you wish it to ‘fire’ (it will be only one of your candidate close times, i.e the next one to occur after the current time). In your case I believe you mentioned the countdown starts 90 seconds before the close time, so this is your “due time” for the waitable timer (next T – 90 secs).
The due time you set must be specified in FILETIME and must be in UTC, not local time. This is fiddly, but not especially difficult.
Calculate the next auto close time, less 90 seconds. Then use DateTimeToSystemTime(localTDateTime, localSYSTEMTIME) to the resulting TDateTime value in a SYSTEMTIME representation which you can then pass to TzSpecificLocalTimeToSystemTime() to convert to a UTC SYSTEMTIME.
From there you simply then convert your UTC SYSTEMTIME to FILETIME (SystemTimeToFileTime() in SysUtils).
The callback proc is a first class proc, not a form method, and must conform to the expected callback signature.
The callback proc will be called in a separate thread so your callback implementation to start the countdown timer must be thread safe. The simplest way to achieve this is to exploit message queues and simply send (or post) a message to the form which in turn responds by starting the countdown timer. To ensure the right window handle is used, this can be passed to the callback proc. Since a HWND fits in a pointer you can just pass the HWND in the pointer directly, by typecasting.
Your callback proc will look something like this:
procedure TimerCallbackProc(aData: Pointer; aTimerLo, aTimerHi: DWORD);
begin
PostMessage(HWND(aData), MM_STARTCOUNTDOWNTIMER, 0, 0);
end;
Where MM_STARTCOUNTDOWNTIMER is a private, WM_USER based message that the form handles to start the countdown timer:
NOTE: Your form must cancel the callback timer when it is closed, either before the timer has ‘fired’ or as a result of it.
Putting all of that together, you should end up with something like:
const
MM_STARTCOUNTDOWNTIMER = WM_USER + 1;
type
TMyForm = class(TForm)
fCloseCountdownTimer: TTimer;
fCloseTimer: HANDLE;
..
procedure MMStartCountdownTimer(var aMessage: TMessage); message MM_STARTCOUNTDOWNTIMER;
end;
procedure TMyForm.FormCreate(Sender: TObject);
begin
..
..
fCloseTimer := CreateWaitableTimer( .. );
SetWaitableTimer( fCloseTimer, dueTime, 0, TimerCallbackproc, Pointer(Handle), TRUE );
end;
procedure TMyForm.FormClose(Sender: TObject);
begin
CancelWaitableTimer( fCloseTimer );
end;
procedure TMyForm.MMStartCountdownTimer(var aMessage: TMessage);
begin
fCloseCountdownTimer.Enabled := TRUE;
end;
NOTE: The final TRUE parameter in the call to SetWaitableTimer() in the code above ensures that if the system is suspended at time that the timer fires, then the system will wake in order to process the timer. If this is not what you want, then simply pass FALSE, and the timer will not wake a sleeping system (but your form will not now close automatically if the due time has been and gone while the system was asleep).
For further and more specific details, I suggest you refer to the Waitable Timer API documentation from Microsoft
2
solved Close the form on certain hours of the day