你可以使用SetTimer函数为自己的Windows程序分配一个计时器,SetTimer包含一个无符号的整形参数,该参数指定了时间间隔的长短,单位是毫秒。
该函数的功能是在指定时间间隔内向你的程序发送一条WM_TIMER消息;
计时器消息不是异步的。
因为计时器是基于硬件计时器的中断,程序员有时候会被误导,认为他们的程序可能会被异步中断打断后被迫去处理wm_timer消息,事实上,Windows处理WM_TIMER消息和处理WM_PAINT消息很类似,这两种消息都是低优先级的,只有当消息队列中没有其他消息时,程序才会收到它们。
使用计时器的三种方法:
方法一:
#define TIMER_SEC 1 #define TIMER_MIN 2 //通过两个SetTimer函数分别设置两个计时器 SetTimer(hwnd,TIMER_SEC,1000,NULL); SetTimer(hwnd,TIMER_MIN,60000,NULL); //WM_TIMER的处理逻辑如下: case WM_TIMER: switch(wParam) { case TIMER_SEC: 每秒处理一次; break;; case TIMER_MIN: 每分钟处理一次 break;; } return 0;
下面看看一个简单的例子,在一秒时间间隔内,改变客户区的背景颜色,发出系统声音。
#include<windows.h> #define ID_TIMER 1 LRESULT CALLBACK WindowProc( HWND hwnd, // handle to window UINT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ); int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // command line int nCmdShow // show state ) { static TCHAR szAppName[]=TEXT("leidemingzi"); HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.cbClsExtra=0; wndclass.cbWndExtra=0; wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.hCursor=LoadCursor(NULL,IDC_ARROW); wndclass.hIcon=LoadIcon(NULL,IDI_ERROR); wndclass.hInstance=hInstance; wndclass.lpfnWndProc=WindowProc; wndclass.lpszClassName=szAppName; wndclass.lpszMenuName=NULL; wndclass.style=CS_HREDRAW|CS_VREDRAW; if(!RegisterClass(&wndclass)) { MessageBox(NULL,TEXT("the program require window nt!"),TEXT("xiao tips"),MB_ICONERROR); return 0; } hwnd=CreateWindow( szAppName, // registered class name TEXT("this is title"), // window name WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // horizontal position of window CW_USEDEFAULT, // vertical position of window CW_USEDEFAULT, // window width CW_USEDEFAULT, // window height NULL, // handle to parent or owner window NULL, // menu handle or child identifier hInstance, // handle to application instance NULL // window-creation data ); ShowWindow(hwnd,nCmdShow); UpdateWindow(hwnd); while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WindowProc( HWND hwnd, // handle to window UINT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { static BOOL fFlipFlop=FALSE; HBRUSH hbrush; HDC hdc; PAINTSTRUCT ps; RECT rect; switch(uMsg) { case WM_CREATE: SetTimer(hwnd,ID_TIMER,1000,NULL);//设置一个计时器,时间间隔为1秒 return 0; case WM_TIMER: MessageBeep(MB_ICONASTERISK);//发出系统声音 fFlipFlop=!fFlipFlop;//取反 InvalidateRect(hwnd,NULL,FALSE);//触发WM_PAINT事件 return 0; case WM_PAINT: hdc=BeginPaint(hwnd,&ps); GetClientRect(hwnd,&rect);//获取客户区大小 hbrush=CreateSolidBrush(fFlipFlop?RGB(255,0,0):RGB(0,0,255));//判断填充红色还是蓝色 FillRect(hdc,&rect,hbrush); EndPaint(hwnd,&ps); DeleteObject(hbrush);//删除画刷 return 0; case WM_DESTROY: KillTimer(hwnd,ID_TIMER);//把Timer删掉 PostQuitMessage(0); return 0; } return DefWindowProc(hwnd,uMsg,wParam,lParam); }
程序虽小,“五脏”俱全。
方法二:
让你只会Windows把计时器消息发送到程序中的另一个函数
收到计时器消息的函数被称为“回调”函数,这是程序中被Windows调用的函数,你告诉Windows这个函数的地址,Windows以后就会调用这个函数。
SetTimer不是以为使用回调功能的Windows函数,CreateDialog,DialogBox都会使用回调函数处理对话框的消息,有几个Windows函数(EnumChildWindow,EnumFont,EnumObject,EnumProps,EnumWindow)会传递枚举信息到回调函数,还有几个不常用的函数GrayString,LineDDA,SetWindowHookEx也要求使用回调函数功能。
//我们给回调函数起名为TimerProc,(你可以取其他名字,函数名不重叠就行) VOID CALLBACK TimerProc(HWND hwnd,UNIT uMsg,UINT iTimerID,DWORD dwTime) { 处理TIMER消息 }
下面看看第二种方法和第一种方法的对比,功能完全一样
#include<windows.h> #define ID_TIMER 1 LRESULT CALLBACK WindowProc( HWND hwnd, // handle to window UINT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ); VOID CALLBACK TimerProc( HWND hwnd, // handle to window UINT uMsg, // message identifier UINT iTimerID , // first message parameter DWORD dwTime // second message parameter ); int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // command line int nCmdShow // show state ) { static TCHAR szAppName[]=TEXT("leidemingzi"); HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.cbClsExtra=0; wndclass.cbWndExtra=0; wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.hCursor=LoadCursor(NULL,IDC_ARROW); wndclass.hIcon=LoadIcon(NULL,IDI_ERROR); wndclass.hInstance=hInstance; wndclass.lpfnWndProc=WindowProc; wndclass.lpszClassName=szAppName; wndclass.lpszMenuName=NULL; wndclass.style=CS_HREDRAW|CS_VREDRAW; if(!RegisterClass(&wndclass)) { MessageBox(NULL,TEXT("the program require window nt!"),TEXT("xiao tips"),MB_ICONERROR); return 0; } hwnd=CreateWindow( szAppName, // registered class name TEXT("this is title"), // window name WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // horizontal position of window CW_USEDEFAULT, // vertical position of window CW_USEDEFAULT, // window width CW_USEDEFAULT, // window height NULL, // handle to parent or owner window NULL, // menu handle or child identifier hInstance, // handle to application instance NULL // window-creation data ); ShowWindow(hwnd,nCmdShow); UpdateWindow(hwnd); while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WindowProc( HWND hwnd, // handle to window UINT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { switch(uMsg) { 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,uMsg,wParam,lParam); } VOID CALLBACK TimerProc( HWND hwnd, // handle to window UINT uMsg, // message identifier UINT iTimerID, // first message parameter DWORD dwTime // second message parameter ) { static BOOL fFlipFlop=FALSE; HBRUSH hbrush; HDC hdc; RECT rect; MessageBeep(MB_ICONASTERISK); fFlipFlop=!fFlipFlop; GetClientRect(hwnd,&rect); hdc=GetDC(hwnd); hbrush=CreateSolidBrush(fFlipFlop?RGB(255,0,0):RGB(0,0,255)); FillRect(hdc,&rect,hbrush); ReleaseDC(hwnd,hdc); DeleteObject(hbrush); }
方法三:
第三种设置计时器的方法和第二种方法相似,只不过SetTimer的hwnd参数被设置成为NULL,而且第二个方法被忽略了,此外这个函数会返回计时器的ID
itimerId=SetTimer(NULL,0,wMsecInterval,TimerProc); KillTimer(NULL,iTimerID);
这种计时器比较少用,如果在程序中,需要不同的时候调用很多次SetTimer,但又不想记录哪些计时器ID已经被使用过,那么这种方法可能会派上用场
在此,不举例子了。
(关注计时器(X),有更精彩的例子)