VC++ 6.0 ebook chapter index
Free counters!

Timer Basics

You can allocate a timer for your Windows program by calling the SetTimer function. SetTimer includes an unsigned integer argument specifying a time-out interval that can range (in theory) from 1 msec (millisecond) to 4,294,967,295 msec, which is nearly 50 days. The value indicates the rate at which Windows sends your program WM_TIMER messages. For instance, an interval of 1000 msec causes Windows to send your program a WM_TIMER message every second.

When your program is done using the timer, it calls the KillTimer function to stop the timer messages. You can program a "one-shot" timer by calling KillTimer during the processing of the WM_TIMER message. The KillTimer call purges the message queue of any pending WM_TIMER messages. Your program will never receive a stray WM_TIMER message following a KillTimer call.

The System and the Timer

The Windows timer is a relatively simple extension of the timer logic built into the PC's hardware and the ROM BIOS. Back in the pre-Windows days of MS-DOS programming, an application could implement a clock or a timer by trapping a BIOS interrupt called the "timer tick." This interrupt occurred every 54.925 msec, or about 18.2 times per second. This is the original 4.772720 MHz microprocessor clock of the original IBM PC divided by 218.

Windows applications do not trap BIOS interrupts. Instead, Windows itself handles the hardware interrupts so that applications don't have to. For every timer that is currently set, Windows maintains a counter value that it decrements on every hardware timer tick. When this counter reaches 0, Windows places a WM_TIMER message in the appropriate application's message queue and resets the counter to its original value.

Because a Windows application receives WM_TIMER messages through the normal message queue, you never have to worry about your program being "interrupted" by a sudden WM_TIMER message while doing other processing. In this way, the timer is similar to the keyboard and mouse: the driver handles the asynchronous hardware interrupt events, and Windows translates these events into orderly, structured, serialized messages.

In Windows 98, the timer has the same 55-msec resolution as the underlying PC timer. In Microsoft Windows NT, the resolution of the timer is about 10 msec.

A Windows application cannot receive WM_TIMER messages at a rate faster than this resolution—about 18.2 times per second under Windows 98 and about 100 times per second under Windows NT. Windows rounds down the time-out interval you specify in the SetTimer call to an integral multiple of clock ticks. For instance, a 1000-msec interval divided by 54.925 msec is 18.207 clock ticks, which is rounded down to 18 clock ticks, which is really a 989-msec interval. For intervals shorter than 55 msec, each clock tick generates a single WM_TIMER message.

Timer Messages Are Not Asynchronous

Because the timer is based on a hardware timer interrupt, programmers sometimes get led astray in thinking that their programs might get interrupted asynchronously to process WM_TIMER messages.

However, the WM_TIMER messages are not asynchronous. The WM_TIMER messages are placed in the normal message queue and ordered with all the other messages. Therefore, if you specify 1000 msec in the SetTimer call, your program is not guaranteed to receive a WM_TIMER message every second or even (as I mentioned earlier) every 989 msec. If your application is busy for more than a second, it will not get any WM_TIMER messages during that time. You can demonstrate this to yourself using the programs shown in this chapter. In fact, Windows handles WM_TIMER messages much like WM_PAINT messages. Both these messages are low priority, and the program will receive them only if the message queue has no other messages.

The WM_TIMER messages are similar to WM_PAINT messages in another respect. Windows does not keep loading up the message queue with multiple WM_TIMER messages. Instead, Windows combines multiple WM_TIMER messages in the message queue into a single message. Therefore, the application won't get a bunch of them at once, although it might get two WM_TIMER messages in quick succession. An application cannot determine the number of "missing" WM_TIMER messages that result from this process.

Thus, a clock program cannot keep time by counting the WM_TIMER messages it receives. The WM_TIMER messages can only inform the application that the time is due to be updated. Later in this chapter, we'll write two clock applications that update themselves every second, and we'll see precisely how this is accomplished.

For convenience, I'll be talking about the timer in terms of "getting a WM_TIMER message every second." But keep in mind that these messages are not precise clock tick interrupts.