13

Is it possible to determine when a TTimer in Delphi will trigger? I know how to calculate this based upon the timer's last run and the timers interval. Unfortunately, the code I am working with has many states and the interval can change in many ways. I would prefer to not have to keep track of when the timer was last enabled and the interval changed, but instead directly access this information from the timer.

Scenario: A timer has a 2 minute interval, 30 seconds have elapsed since it was last enabled, how do I find out in code that in 90 seconds the timer event will trigger again?

Is it possible to get this information from the timer directly? Or perhaps the OS? The timer component must "know" when it will be triggered next. How does it know? Is this information I can access?

sse
  • 987
  • 1
  • 11
  • 30
  • 1
    @David. Thank you for the nudge and the reference. I am still pretty new here. I have gone through the responses and made the necessary up votes/accepts. – sse Aug 31 '11 at 21:51
  • 1
    sse: You should be aware that TTimer events don't always come in every 30 seconds, when you configure them to come in every 30 seconds. According to MSDN, The" WM_TIMER message is a low-priority message. The GetMessage and PeekMessage functions post this message only when no other higher-priority messages are in the thread's message queue.". How's that for unpredictable? There can be delays, and the length of these delays is potentially unbounded, under high load conditions. – Warren P Sep 01 '11 at 14:46
  • @sse: this is a bit off-topic but if you're looking for more accuracy, there are the timers function in mmsystem: http://msdn.microsoft.com/en-us/library/dd743611(v=vs.85).aspx. – az01 Sep 01 '11 at 15:09
  • I have implemented a thread-based timer that does not use low-priority messages and adjusts each period to maintain a precise average event time, e.g. if the interval is 1000 ms and the next timer event takes 1003 ms (due to the vagaries of the OS) then the next interval will be adjusted down to 997 ms. – Misha Sep 01 '11 at 15:21
  • @sse, download the framework from my web site and look for the unit CsiTimerCmp.pas – Misha Sep 03 '11 at 11:04
  • @Misha, there is no point to insistently advertize your library here. Neither TTimer nor underlying OS subsystem provide this information. Period. – Premature Optimization Sep 08 '11 at 02:47

2 Answers2

15

There's absolutely no way to query a Windows timer for information of this nature. You will simply have to keep track of this yourself.

I would do this by wrapping up the TTimer with composition and not inheritance. You can then be sure that you will capture all modifications to the timer state.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • That's pretty definitive. Do you know how the timer keeps track of when it will trigger again? – sse Aug 31 '11 at 21:18
  • 1
    No I don't know that. That's private internal implementation details of Windows. – David Heffernan Aug 31 '11 at 21:23
  • @sse - Timers are simply targets of the wm_timer windows message. They don't know when, or if, the next one will arrive. Read the docs: http://msdn.microsoft.com/en-us/library/ms644902(v=vs.85).aspx – Chris Thornton Aug 31 '11 at 21:34
4

In this case I recommend you switch from a TTimer which uses Windows timers, to a thread based TTimer-style component. Then you can query the time until the next event.

Alternative; If you want a simple ugly hack, then change your Timer interval to 1 second instead of 120 seconds, and do a countdown yourself:

   const
     CounterPreset = 120;

   ...

   procedure TForm1.FormCreate(Sender:TObject);
   begin
      FCounter := CounterPreset;
      Timer1.Interval := 1000;
      Timer1.Enabled := true;
   end;

   procedure TForm1.Timer1Timer(Sender);
   begin
           Dec(FCounter);
           if (FCounter<=0) then
           begin
              DoRealTimerCodeHere;
              FCounter := CounterPreset;
           end;
   end;

   function  TForm1.TimeLeft:Integer;
   begin
         result := FCounter;
   end;

This will be inaccurate subject to the limitations of the WM_TIMER message, documented only vaguely at MSDN here. Real experience shows that WM_TIMER should only be used for things that don't need to happen at all, and should be used as a convenience, not as a hard-timing system.

Warren P
  • 65,725
  • 40
  • 181
  • 316
  • This doesn't answer the question asked. It provides an alternative, and should have been a comment to the original question instead. – Ken White Sep 01 '11 at 01:40
  • Wrapping up the logic the OP needs in a TTimer-like object (like the one I linked to) is exactly an answer to the question. How do I do the impossible? You don't, you do the possible. – Warren P Sep 01 '11 at 02:26
  • 2
    As I said, that would have been a great comment. The first sentence of the question asked (specifically) "Is it possible to determine when a **TTimer** in Delphi will trigger?". Your answer has **nothing** to do with answering that specific question. "How do I get my dog to use a litterbox?" "Buy a cat." "How do I configure this in Windows?" "Install Linux instead." – Ken White Sep 01 '11 at 02:29
  • 5
    @Ken, I'll take Warren's response over your comments any day of the week. The biggest mistake is to assume that the question is perfect and that the OP has thought through all the options before asking the question. I'd be mighty peeved if I never got responses like Warren's because it was not specifically asked in the question. I actually have a thread-based timer with the same interface as the TTimer component that can be swapped directly and easily to offer this information. – Misha Sep 01 '11 at 06:19
  • I'm fine with outside the box thinking but I'd never endorse solutions involving Sleep. – David Heffernan Sep 01 '11 at 06:41
  • 3
    There is a habit on stackoverflow of pedantically answering the question (as pedantically as possible without questioning or allowing for any change in the stated terms at hand), instead of solving the user's problem. Ken represents the pedants, I represent the pragmatists. We periodically disagree. Ken equates using another class named TTimer with "installing linux" or putting my dog in a blender and getting a cat. As you can see, pedantry. – Warren P Sep 01 '11 at 08:43
  • @David, what's wrong with Sleep? If you mean that the actual Sleep time is not accurate below 10ms, the same is true for TTimer events. – iamjoosy Sep 01 '11 at 10:18
  • 1
    @@iamjoosy I just don't really believe in Sleep. I prefer proper waits rather than semi-busy waits. Don't like burning clock cycles for no good reason. In this case there is absolutely no need to do so. – David Heffernan Sep 01 '11 at 10:29
  • 1
    @Warren: It has nothing to do with being pedantic. It has to do with helping the person with the question asked. If you do that first, I'm all for providing alternatives as an addendum to your answer. However, doing absolutely nothing to answer the question asked (thus the Linux and pet references, which demonstrate your technique in this question precisely) doesn't qualify as an answer. SO works by answering questions that people post, and *then* steering them to better solutions. – Ken White Sep 01 '11 at 11:01
  • @Ken there is plenty of room for both approaches to answering. Both add value. – David Heffernan Sep 01 '11 at 13:34
  • 4
    The only answer to the original question is "No you can't". I don't think that adds any value to my answer though. – Warren P Sep 01 '11 at 13:48
  • @David; There are windows multimedia timer services that could be used, without any sleeps, but I don't believe you can query them. If you want to query a timer, you need to be the guy who twiddles the numbers yourself. – Warren P Sep 01 '11 at 14:45
  • @Warren Well, you could just do it my way. – David Heffernan Sep 01 '11 at 14:54
  • @Warren, thank you for the alternative suggestion, I do appreciate it. – sse Sep 02 '11 at 14:53
  • @Misha, thank you for the offer. I wouldn't mind taking a look at your code, if you're offering. I am wondering if the JVTimer in Jedi Tools may have a more direct way of retrieving what I am looking for. I will be checking this. – sse Sep 02 '11 at 14:55
  • @sse, download the framework from my web site and look for the unit CsiTimerCmp.pas – Misha Sep 03 '11 at 11:04