|
Beep.c - Revised/* beep.c. Periodic alarm. */ /* Usage: beep period-in-seconds */ /* The time period is in seconds and includes the time the alarm is on. The beeper goes off periodically until the user presses the message box button. */ /* Note that very little is achieved here as the main program cannot do anything until the user presses the button, causing a return from MessageBox. This is not very satisfactory for a console application without its own main message loop. As an alternative, you could simply have a loop such as the following: while (TRUE) { Sleep (Period); .... Do whatever is in the callback function } Now, if you really want to do something else but still have the action of the callback function carried out periodically, you could create a separate process or thread containing the above loop. Threads are discussed in Chapter 10, and the Server program in Chapter 9 has a separate process to carry out periodic actions. You might want to give the thread or process a high priority (also see Chapter 10). TimeBeep.cThis program uses a "waitable" timer, which is a named, sharable, securable object. Waitable timers were introduced in Windows NT Version 5.0 and are not available in Windows 95/98. Furthermore, library support was introduced in Visual C++ Version 5.0. The naming is similar to Chapter 6's memory-mapped objects and Chapter 11's synchronization objects (semaphores, events, and mutexes). First, the program calls CreateWaitableTimer to obtain a handle. The second parameter determines whether or not the timer is a "synchronization timer" or a "manual reset notification timer" (the documentation does not explain the difference, so some remarks will follow). This example uses a synchronization timer, but you can change the comment to obtain a notification timer. Notice that there is also an OpenWaitableTimer function. Next, SetWaitableTimer specifies the initial signal time as either a positive absolute time or a negative relative time; we use a relative time. The interval between signals is also specified. FILETIMEs are used to specify these times. Finally, there is a parameter specifying the time-out function (completion routine) to be called. Having set the timer, you can now either call SleepEx (see Chapter 13) or WaitForSingleObjectEx (also see Chapter 13). The first technique, the default in this example (the wait is commented out), blocks this thread until the timer is signaled and the timer function completes. Here are the four combinations and the results, with some interpretation:
Conclusion: You should use SleepEx regardless of the timer type. Notification timer behavior is counter-intuitive. I would have expected that such a timer would be required invoke the completion routine after the thread enters the alertable wait state. Conversely, I would have thought that a synchronization timer would be used with a simple wait function (no Ex suffix); this will, in fact, work correctly if you do not have a completion routine. Use CancelWaitableTimer to cancel the last effect of a previous SetWaitableTimer, although it will not change the signaled state of the timer. Use another SetWaitableTimer call to do that. Additional Comments: A manual reset timer remains reset until you set it again (with SetWaitableTimer). Therefore, the MR timer would appear to have no utility as a periodic timer. Thanks to Andrew Tucker for some helpful comments on this section. /* Chapter 8 - New Program. TimeBeep.c. Periodic alarm. This is an alternative implementation to the one used in beep.c, which used the main message loop. You may want to read Chapter 13 (p. 277) first as the "alertable wait functions" are required. ALSO: This program uses a console control handler to catch control-c signals; this subject is covered at the end of Chapter 8.*/ /* Usage: TimeBeep Period. */ /* This implementation uses the "waitable timers" introduced in NT 4.0 and supported by Visual C++ Ver 5.0. The waitable timers are sharable named objects (their names share the same space used by synchronization and memory mapped objects). The waitable timer is a synchronization object that you can wait on for the timer to signal. Alternatively, you can have a completion routine executed in the same thread by entering an "alertable wait state" just as with extended, ("completion routine") I/O (see Chapter 13). */ #define _WIN32_WINNT 0x0400 /* Required in <winbase.h> to define waitable timer functions. This is not explained in the documentation. It indicates that you need NT 4.0. */ #include "EvryThng.h" static BOOL WINAPI Handler (DWORD CntrlEvent); static VOID APIENTRY Beeper (LPVOID, DWORD, DWORD); volatile static BOOL Exit = FALSE; HANDLE hTimer; int _tmain (int argc, LPTSTR argv []) { DWORD Count = 0, Period; LARGE_INTEGER DueTime; if (argc >= 2) Period = _ttoi (argv [1]) * 1000; else ReportError (_T ("Usage: beep period"), 1, FALSE); if (!SetConsoleCtrlHandler (Handler, TRUE)) ReportError (_T ("Error setting event handler"), 2, TRUE); DueTime.QuadPart = -(LONGLONG)Period * 10000; /* Due time is negative for first time out relative to current time. Period is in ms (10**-3 sec) whereas the due time is in 100 ns (10**-7 sec) units to be consistent with a FILETIME. */ hTimer = CreateWaitableTimer (NULL, /* Security attributes */ /*TRUE*/ FALSE, /* Not manual reset (not a "notification timer") but a "synchronization timer". Documentation does not explain the distinction, but see comments at top of this page. */ NULL /* Do not name the timer - name space is shared with events, mutexes, semaphores, and mem map objects */); if (hTimer == NULL) ReportError (_T ("Failure creating waitable timer"), 3, TRUE); if (!SetWaitableTimer (hTimer, &DueTime /* relative time of first signal */, Period /* Time period in ms */, Beeper /* Timer function */, &Count /* Parameter passed to timer function */, TRUE /* Does not apply - do not use it. */)) ReportError (_T ("Failure setting waitable timer"), 4, TRUE); /* Enter the main loop */ while (!Exit) { _tprintf (_T("Count = %d\n"), Count); /* Count is increased in the timer routine */ /* Enter an alertable wait state, enabling the timer routine. The timer handle is a synchronization object, so you can also wait on it. */ SleepEx (INFINITE, TRUE); // WaitForSingleObjectEx (hTimer, INFINITE, TRUE); /* See comment at the top of this page */ } _tprintf (_T("Shut down. Count = %d"), Count); CancelWaitableTimer (hTimer); CloseHandle (hTimer); return 0; } static VOID APIENTRY Beeper (LPVOID lpCount, DWORD dwTimerLowValue, DWORD dwTimerHighValue) { *(LPDWORD)lpCount = *(LPDWORD)lpCount + 1; Beep (1000 /* Frequency */, 250 /* Duration (ms) */); return; } BOOL WINAPI Handler (DWORD CntrlEvent) { Exit = TRUE; return TRUE; } |