当前位置:文档之家› c++定时器

c++定时器

定时器今天我们来讨论下c++中的定时器(SetTimer)一些基础的知识和用法:你可以通过呼叫SetTimer函数为您的Windows程序分配一个定时器。

SetTimer有一个时间间隔范围为1毫秒到4,294,967,295毫秒(将近50天)的整数型态参数,这个值指示Windows每隔多久时间给您的程序发送WM_TIMER消息。

例如,如果间隔为1000毫秒,那么Windows将每秒给程序发送一个WM_TIMER消息。

当您的程序用完定时器时,它呼叫KillTimer函数来停止定时器消息。

在处理WM_TIMER 消息时,您可以通过呼叫KillTimer函数来编写一个「限用一次」的定时器。

KillTimer呼叫清除消息队列中尚未被处理的WM_TIMER消息,从而使程序在呼叫KillTimer之后就不会再接收到WM_TIMER消息。

系统和定时器Windows定时器是PC硬件和ROM BIOS架构下之定时器一种相对简单的扩充。

回到Windows 以前的MS-DOS程序写作环境下,应用程序能够通过拦截者称为timer tick的BIOS中断来实作时钟或定时器。

一些为MS-DOS编写的程序自己拦截这个硬件中断以实作时钟和定时器。

这些中断每54.915毫秒产生一次,或者大约每秒18.2次。

这是原始的IBM PC的微处理器时脉值4.772720 MHz 被218所除而得出的结果。

Windows应用程序不拦截BIOS中断,相反地,Windows本身处理硬件中断,这样应用程序就不必进行处理。

对于目前拥有定时器的每个程序,Windows储存一个每次硬件timer tick减少的计数。

当这个计数减到0时,Windows在应用程序消息队列中放置一个WM_TIMER消息,并将计数重置为其最初值。

因为Windows应用程序从正常的消息队列中取得WM_TIMER消息,所以您的程序在进行其它处理时不必担心WM_TIMER消息会意外中断了程序。

在这方面,定时器类似于键盘和鼠标。

驱动程序处理异步硬件中断事件,Windows把这些事件翻译为规律、结构化和顺序化的消息。

在Windows 98中,定时器与其下的PC定时器一样具有55毫秒的分辨率。

在Microsoft Windows NT中,定时器的分辨率为10毫秒。

Windows应用程序不能以高于这些分辨率的频率(在Windows 98下,每秒18.2次,在Windows NT下,每秒大约100次)接收WM_TIMER消息。

在SetTimer呼叫中指定的时间间隔总是截尾后tick数的整数倍。

例如,1000毫秒的间隔除以54.925毫秒,得到18.207个tick,截尾后是18个tick,它实际上是989毫秒。

对每个小于55毫秒的间隔,每个tick都会产生一个WM_TIMER消息。

定时器消息不是异步的因为定时器使用硬件定时器中断,程序写作者有时会误解,认为他们的程序会异步地被中断来处理WM_TIMER消息。

然而,WM_TIMER消息并不是异步的。

WM_TIMER消息放在正常的消息队列之中,和其它消息排列在一起,因此,如果在SetTimer呼叫中指定间隔为1000毫秒,那么不能保证程序每1000毫秒或者989毫秒就会收到一个WM_TIMER消息。

如果其它程序的执行事件超过一秒,在此期间内,您的程序将收不到任何WM_TIMER消息。

您可以使用本章的程序来展示这一点。

事实上,Windows 对WM_TIMER消息的处理非常类似于对WM_PAINT消息的处理,这两个消息都是低优先级的,程序只有在消息队列中没有其它消息时才接收它们。

WM_TIMER还在另一方面和WM_PAINT相似:Windows不能持续向消息队列中放入多个WM_TIMER消息,而是将多余的WM_TIMER消息组合成一个消息。

因此,应用程序不会一次收到多个这样的消息,尽管可能在短时间内得到两个WM_TIMER消息。

应用程序不能确定这种处理方式所导致的WM_TIMER消息「遗漏」的数目。

这样,WM_TIMER消息仅仅在需要更新时才提示程序,程序本身不能经由统计WM_TIMER 消息的数目来计时(在本章后面,我们将编写两个每秒更新一次的时钟程序,并可以看到如何做到这一点)。

为了方便起见,下面在讨论时钟时,我将使用「每秒得到一次WM_TIMER消息」这样的叙述,但是请记住,这些消息并非精确的tick中断。

定时器的使用:三种方法如果您需要在整个程序执行期间都使用定时器,那么您将得从WinMain函数中或者在处理WM_CREATE消息时呼叫SetTimer,并在退出WinMain或响应WM_DESTROY消息时呼叫KillTimer。

根据呼叫SetTimer时使用的参数,可以下列三种方法之一使用定时器。

方法举例这是最方便的一种方法,它让Windows把WM_TIMER消息发送到应用程序的正常窗口消息处理程序中,SetTimer呼叫如下所示:SetTimer (hwnd, 1, uiMsecInterval, NULL) ;第一个参数是其窗口消息处理程序将接收WM_TIMER消息的窗口句柄。

第二个参数是定时器ID,它是一个非0数值,在整个例子中假定为1。

第三个参数是一个32位无正负号整数,以毫秒为单位指定一个时间间隔,一个60,000的值将使Windows每分钟发送一次WM_TIMER消息。

您可以通过呼叫KillTimer (hwnd, 1) ;在任何时刻停止WM_TIMER消息(即使正在处理WM_TIMER消息)。

此函数的第二个参数是SetTimer呼叫中所用的同一个定时器ID。

在终止程序之前,您应该响应WM_DESTROY消息停止任何活动的定时器。

当您的窗口消息处理程序收到一个WM_TIMER消息时,wParam参数等于定时器的ID值(上述情形为1),lParam参数为0。

如果需要设定多个定时器,那么对每个定时器都使用不同的定时器ID。

wParam的值将随传递到窗口消息处理程序的WM_TIMER消息的不同而不同。

为了使程序更具有可读性,您可以使用#define叙述定义不同的定时器ID:#define TIMER_SEC 1#define TIMER_MIN 2然后您可以使用两个SetTimer呼叫来设定两个定时器:SetTimer (hwnd, TIMER_SEC, 1000, NULL) ;SetTimer (hwnd, TIMER_MIN, 60000, NULL) ;WM_TIMER的处理如下所示:caseWM_TIMER:switch (wParam){case TIMER_SEC://每秒一次的处理break ;case TIMER_MIN://每分钟一次的处理break ;}return 0 ;如果您想将一个已经存在的定时器设定为不同的时间间隔,您可以简单地用不同的时间值再次呼叫SetTimer。

在时钟程序里,如果显示秒或不显示秒是可以选择的,您就可以这样做,只需简单地将时间间隔在1000毫秒和60 000毫秒间切换就可以了。

程序8-1显示了一个使用定时器的简单程序,名为BEEPER1,定时器的时间间隔设定为1秒。

当它收到WM_TIMER消息时,它将显示区域的颜色由蓝色变为红色或由红色变为蓝色,并通过呼叫MessageBeep函数发出响声。

(虽然MessageBeep通常用于MessageBox,但它确实是一个全功能的鸣叫函数。

在有声卡的PC机上,一般可以使用不同的MB_ICON参数作为MessageBeep的一个参数以用于MessageBox,来播放使用者在「控制台」的「声音」程序中选择的不同声音)。

BEEPER1在窗口消息处理程序处理WM_CREATE消息时设定定时器。

在处理WM_TIMER消息处理期间,BEEPER1呼叫MessageBeep,翻转bFlipFlop的值并使窗口无效以产生WM_PAINT消息。

在处理WM_PAINT消息处理期间,BEEPER1通过呼叫GetClientRect获得窗口大小的RECT结构,并通过呼叫FillRect改变窗口的颜色。

代码#include <windows.h>#define ID_TIMER 1LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){static TCHAR szAppName[] = TEXT ("Beeper1") ;HWND hwnd ;MSG msg ;WNDCLASS wndclass ;wndclass.style = CS_HREDRAW | CS_VREDRAW ;wndclass.lpfnWndProc = WndProc ;wndclass.cbClsExtra = 0 ;wndclass.cbWndExtra = 0 ;wndclass.hInstance = hInstance ;wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;wndclass.lpszMenuName = NULL ;wndclass.lpszClassName = szAppName ;if (!RegisterClass (&wndclass)){MessageBox ( NULL, TEXT ("Program requires Windows NT!"),szAppName, MB_ICONERROR) ;return 0 ;}hwnd = CreateWindow (szAppName, TEXT ("Beeper1 Timer Demo"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;ShowWindow (hwnd, iCmdShow) ;UpdateWindow (hwnd) ;while (GetMessage (&msg, NULL, 0, 0)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;}return msg.wParam ;}LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam) {static BOOL fFlipFlop = FALSE ;HBRUSH hBrush ;HDC hdc ;PAINTSTRUCT ps ;RECT rc ;switch (message){case WM_CREATE:SetTimer (hwnd, ID_TIMER, 1000, NULL) ;return 0 ;case WM_TIMER :MessageBeep (-1) ;fFlipFlop = !fFlipFlop ;InvalidateRect (hwnd, NULL, FALSE) ;return 0 ;case WM_PAINT :hdc = BeginPaint (hwnd, &ps) ;GetClientRect (hwnd, &rc) ;hBrush = CreateSolidBrush (fFlipFlop ? RGB(255,0,0) : RGB(0,0,255)) ;FillRect (hdc, &rc, hBrush) ;EndPaint (hwnd, &ps) ;DeleteObject (hBrush) ;return 0 ;case WM_DESTROY :KillTimer (hwnd, ID_TIMER) ;PostQuitMessage (0) ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;}因为BEEPER1每次收到WM_TIMER消息时,都用颜色的变换显示出来,所以您可以通过呼叫BEEPER1来查看WM_TIMER消息的性质,并完成Windows内部的一些其它操作。

相关主题