若是您须要在整个程序执行期间都使用定时器,那么您将得从WinMain函数中或者在处理WM_CREATE消息时呼叫SetTimer,并在退出WinMain或响应WM_DESTROY消息时呼叫KillTimer。根据呼叫SetTimer时使用的参数,能够下列三种方法之一使用定时器。html
方法一windows
这是最方便的一种方法,它让Windows把WM_TIMER消息发送到应用程序的正常窗口消息处理程序中,SetTimer呼叫以下所示:ide
SetTimer (hwnd, 1, uiMsecInterval, NULL) ;
第一个参数是其窗口消息处理程序将接收WM_TIMER消息的窗口句柄。第二个参数是定时器ID,它是一个非0数值,在整个例子中假定为1。第三个参数是一个32位无正负号整数,以毫秒为单位指定一个时间间隔,一个60,000的值将使Windows每分钟发送一次WM_TIMER消息。函数
您能够经过呼叫ui
KillTimer (hwnd, 1) ;
在任什么时候刻中止WM_TIMER消息(即便正在处理WM_TIMER消息)。此函数的第二个参数是SetTimer呼叫中所用的同一个定时器ID。在终止程序以前,您应该响应WM_DESTROY消息中止任何活动的定时器。spa
当您的窗口消息处理程序收到一个WM_TIMER消息时,wParam参数等于定时器的ID值(上述情形为1),lParam参数为0。若是须要设定多个定时器,那么对每一个定时器都使用不一样的定时器ID。wParam的值将随传递到窗口消息处理程序的WM_TIMER消息的不一样而不一样。为了使程序更具备可读性,您可使用#define叙述定义不一样的定时器ID:.net
#define TIMER_SEC 1 #define TIMER_MIN 2
而后您可使用两个SetTimer呼叫来设定两个定时器:orm
SetTimer (hwnd, TIMER_SEC, 1000, NULL) ; SetTimer (hwnd, TIMER_MIN, 60000, NULL) ;
WM_TIMER的处理以下所示:htm
caseWM_TIMER: switch (wParam) { case TIMER_SEC: //每秒一次的处理 break ; case TIMER_MIN: //每分钟一次的处理 break ; } return 0 ;
若是您想将一个已经存在的定时器设定为不一样的时间间隔,您能够简单地用不一样的时间值再次呼叫SetTimer。在时钟程序里,若是显示秒或不显示秒是能够选择的,您就能够这样作,只需简单地将时间间隔在1000毫秒和60 000毫秒间切换就能够了。ip
程序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改变窗口的颜色。
程序8-1 BEEPER1
BEEPER1.C /*------------------------------------------------------------------------- BEEPER1.C -- Timer Demo Program No. 1 (c) Charles Petzold, 1998 -------------------------------------------------------------------------*/ #include <windows.h> #define ID_TIMER 1 LRESULT 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内部的一些其它操做。
例如,首先呼叫控制台的 显示器程序,选择效果,肯定 拖曳时显示窗口内容复选框没有被选中。如今,试着移动或者缩放BEEPER1窗口,这将致使程序进入「模态消息循环」。Windows经过在内部消息而非您程序的消息循环中拦截全部消息,来禁止对移动或者缩放操做的任何干扰。经过此循环到达程序窗口的大多数消息都被丢弃,这就是BEEPER1中止蜂鸣的缘由。当完成了移动与缩放以后,您将会注意到BEEPER1不能取得它所丢弃的全部WM_TIMER消息,尽管前两个消息的间隔可能少于1秒。
在「拖曳时显示窗口内容」复选框被选中时,Windows中,的模态消息循环会试图给您的窗口消息处理程序传递一些丢失的消息。这样作有时工做得很好,有时却不行。
方法二
设定定时器的第一种方法是把WM_TIMER消息发送到一般的窗口消息处理程序,而第二种方法是让Windows直接将定时器消息发送给您程序的另外一个函数。
接收这些定时器消息的函数被称为「callback」函数,这是一个在您的程序之中可是由Windows呼叫的函数。您先告诉Windows此函数的地址,而后Windows呼叫此函数。这看起来也很熟悉,由于程序的窗口消息处理程序实际上也是一种callback函数。当注册窗口类别时,要将函数的地址告诉Windows,当发送消息给程序时,Windows会呼叫此函数。
SetTimer并不是是惟一使用callback函数的Windows函数。CreateDialog和DialogBox函数(将在第十一章中介绍)使用callback函数处理对话框中的消息;有几个Windows函数(EnumChildWindow、EnumFonts、EnumObjects、EnumProps和EnumWindow)把列举信息传递给callback函数;还有几个不那么经常使用的函数(GrayString、LineDDA和SetWindowHookEx)也要求callback函数。
像窗口消息处理程序同样,callback函数也必须定义为CALLBACK,由于它是由Windows从程序的程序代码段呼叫的。callback函数的参数和callback函数的传回值取决于callback函数的目的。跟定时器有关的callback函数中,输入参数与窗口消息处理程序的输入参数同样。定时器callback函数不向Windows传回值。
咱们把如下的callback函数称为TimerProc(您可以选择与其它一些用语不会发生冲突的任何名称),它只处理WM_TIMER消息:
VOID CALLBACK TimerProc ( HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime) { 处理WM_TIMER消息 }
TimerProc的参数hwnd是在呼叫SetTimer时指定的窗口句柄。Windows只把WM_TIMER消息送给TimerProc,所以消息参数老是等于WM_TIMER。iTimerID值是定时器ID,dwTimer值是与从GetTickCount函数的传回值相容的值。这是自Windows启动后所通过的毫秒数。
在BEEPER1中已经看到过,用第一种方法设定定时器时要求下面格式的SetTimer呼叫:
SetTimer (hwnd, iTimerID, iMsecInterval, NULL) ;
您使用callback函数处理WM_TIMER消息时,SetTimer的第四个参数由callback函数的地址取代,以下所示:
SetTimer (hwnd, iTimerID, iMsecInterval, TimerProc) ;
咱们来看看一些范例程序代码,这样您就会了解这些东西是如何组合在一块儿的。在功能上,除了Windows发送一个定时器消息给TimerProc而非WndProc以外,程序8-2所示的BEEPER2程序与BEEPER1是相同的。注意,TimerProc和WndProc一块儿被声明在程序的开始处。
程序8-2 BEEPER2 BEEPER2.C /*--------------------------------------------------------------------------- BEEPER2.C -- Timer Demo Program No. 2 (c) Charles Petzold, 1998 ---------------------------------------------------------------------------*/ #include <windows.h> #define ID_TIMER 1 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; VOID CALLBACK TimerProc (HWND, UINT, UINT, DWORD ) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static char szAppName[] = "Beeper2" ; 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, "Beeper2 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) { switch (message) { case WM_CREATE: SetTimer (hwnd, ID_TIMER, 1000, TimerProc) ; return 0 ; case WM_DESTROY: KillTimer (hwnd, ID_TIMER) ; PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } VOID CALLBACK TimerProc (HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime) { static BOOL fFlipFlop = FALSE ; HBRUSH hBrush ; HDC hdc ; RECT rc ; MessageBeep (-1) ; fFlipFlop = !fFlipFlop ; GetClientRect (hwnd, &rc) ; hdc = GetDC (hwnd) ; hBrush = CreateSolidBrush (fFlipFlop ? RGB(255,0,0) : RGB(0,0,255)) ; FillRect (hdc, &rc, hBrush) ; ReleaseDC (hwnd, hdc) ; DeleteObject (hBrush) ; }
方法三
设定定时器的第三种方法相似于第二种方法,只是传递给SetTimer的hwnd参数被设定为NULL,而且第二个参数(一般为定时器ID)被忽略了,最后,此函数传回定时器ID:
iTimerID = SetTimer (NULL, 0, wMsecInterval, TimerProc) ;
若是没有可用的定时器,那么从SetTimer传回的iTimerID值将为NULL。
KillTimer的第一个参数(一般是窗口句柄)也必须为NULL,定时器ID必须是SetTimer的传回值:
KillTimer (NULL, iTimerID) ;
传递给TimerProc定时器函数的hwnd参数也必须是NULL。这种设定定时器的方法不多被使用。若是在您的程序在不一样时刻有一系列的SetTimer呼叫,而又不但愿追踪您已经用过了那些定时器ID,那么使用此方法是很方便的。
既然您已经知道了如何使用Windows定时器,就能够开始讨论一些有用的定时器程序了。
转载自:http://c.biancheng.net/cpp/html/1280.html