Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
260 views
in Technique[技术] by (71.8m points)

delphi - Close the form on certain hours of the day

I would like my form to close when the time is 06:00, 12:00 and 24:00. But before it closes I would like to display a progress bar showing how much time is remaining before the form closes (When the progress bar reaches 100% - the form closes). How can I do this?

Edit: I was doing this:

procedure TMainForm.Timer1Timer(Sender: TObject);
begin
Timer1.Enabled := False;
 AdvOfficeStatusBar1.Panels[4].Progress.Position := AdvOfficeStatusBar1.Panels[4].Progress.Position +1;
 if AdvOfficeStatusBar1.Panels[4].Progress.Position = 100 then begin
 MainForm.Close;
 end;
 Timer1.Enabled := True;
end;

So what I need is perhaps another timer which would detect the time of the day and fire on designated time the progress bar (Timer1.Enabled := True;). How do you detect the right time in code?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...