简介
定时器相对来讲比较简单,主要用于我们程序中的定时功能,当我们需要使用定时器的时候,需要用一个SetTimer函数来建立一个定时器,如果建立成功,系统会在指定的时间内调用SetTimer函数所指定的回调函数,或者向指定的窗口过程发送WM_TIMER消息。需要注意的是,WM_TIMER是一个低级别的消息,也就是说Windows只有在消息队列中没有其他的消息的情况下才会发送WM_TIMER消息,如果窗口过程忙于处理其他消息而没有返回,使得消息队列中有消息积累起来,那么WM_TIMER消息就会被丢弃,在消息队列再度空闲的时候,被丢弃的WM_TIMER消息也不会被补发,另外,消息队列中不会有多条WM_TIMER消息,如果消息队列中已经有了一条WM_TIMER消息,还没有来得及处理,又到了定时的时刻,那么两条WM_TIMER消息会被合并。所以说,我们不能够实用定时器来完成某一项高精度的事情。
使用方法
在讨论定时器的使用方法之前,我们首先来看一下,SetTimer函数。
UINT_PTR SetTimer( HWND hWnd, UINT_PTR nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc );
- hWnd:需要发送定时器消息的窗口句柄,如果为NULL,则第二个参数被忽略。
- nIDEvent:用户指定的定时器ID,我们可能在程序中设置不止一个定时器,这个参数用来区分是哪一个定时器。
- uElapse:以毫秒为单位的一个时间,在每隔这个时间后会发送一个定时器消息。
- lpTimerFunc:定时器过程,当间隔时间到,会调用这个过程,如果为NULL,则系统会发送一个WM_TIMER消息到应用程序的消息队列中去。定时器过程函数一般定义为:
VOID CALLBACK TimerProc(
HWND hwnd, // handle to window
UINT uMsg, // WM_TIMER message
UINT_PTR idEvent, // timer identifier
DWORD dwTime // current system time
);
当我们不再需要一个定时器的时候,我们需要使用KillTimer函数来结束一个定时器,这个函数有两个参数,第一个参数传入窗口句柄,第二个参数表示定时器的ID,以表示要结束的是哪一个定时器。
提到定时器,就不得不说的是,Windows为我们提供的获取系统时间的函数。获取系统时间的函数有2个,一个是GetLocalTime函数,一个是GetSystemTime函数,GetLocalTime返回当前的时间,GetSystemTime返回当前的格林威治标准时间,这两个函数都需要传入一个指向SYSTEMTIME结构的指针,函数会把时间数据返回到这个缓冲区中。与此相对应的,我们可以使用SetLocalTime函数和SetSystemTime函数来设置系统时间。
了解了这么多,我们现在可以看看定时器的使用方法了,设置定时器一般有两种使用方法,第一种类似:
SetTimer( hwnd, ID_TIMER1, 1000, NULL );
这样我们就设置了一个每隔1秒钟就会发送一个WM_TIMER消息的定时器,其ID是ID_TIMER1,这时,别忘了在程序中处理WM_TIMER消息。第二种如下使用:
SetTimer( hwnd, ID_TIMER2, 1000, TimerProc );
同样,我们定义了一个间隔时间是1秒钟的定时器,只不过,在每1秒钟后,其不会发送定时器消息,而是调用TimerProc函数,TimeProc函数的定义如上所示。最后,在结束的时候,这两种情况都需要调用KillTimer函数。
实例
我们通过一个一个时钟程序来观察一下定时器的使用,其运行界面和完整代码如下所示:(除去资源定义)
// clock.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include
#include "resource.h"
#define ID_TIMER 1
#define PI 3.14159
LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
void ShowTime( HWND, HDC, int, int );
void DrawDot( HDC, double, int, LONG, POINT );
void DrawLine( HDC, double, int, LONG, POINT );
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
WNDCLASSEX wndcls = {0};
wndcls.cbSize = sizeof(WNDCLASSEX);
wndcls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndcls.hCursor = LoadCursor(NULL, IDC_ARROW);
wndcls.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(ICO_MAIN));
wndcls.hInstance = hInstance;
wndcls.lpfnWndProc = WndProc;
wndcls.lpszClassName = "Clock";
wndcls.style = CS_HREDRAW | CS_VREDRAW;
RegisterClassEx( &wndcls );
HWND hWnd = CreateWindowEx( WS_EX_CLIENTEDGE, "Clock", "Clock",
WS_OVERLAPPEDWINDOW, 100, 100, 250, 270,
NULL, NULL, hInstance, NULL );
ShowWindow( hWnd, SW_SHOWNORMAL );
UpdateWindow( hWnd );
while( GetMessage(&msg,NULL,0,0) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
HDC hdc;
PAINTSTRUCT ps;
static int cxWidth,cyHight;
switch(uMsg)
{
case WM_SIZE:
cxWidth = LOWORD(lParam);
cyHight = HIWORD(lParam);
return 0;
case WM_TIMER:
// 每隔一秒钟刷新一次界面,发送WM_PAINT消息
InvalidateRect( hWnd, NULL, TRUE );
return 0;
case WM_PAINT:
hdc = BeginPaint( hWnd, &ps );
// 显示当前时间
ShowTime( hWnd, hdc, cxWidth, cyHight);
EndPaint( hWnd, &ps );
return 0;
case WM_CREATE:
// 定义一个间隔时间为1秒的定时器。
SetTimer( hWnd, ID_TIMER, 1000, NULL);
return 0;
case WM_CLOSE:
// 结束一个定时器。
KillTimer( hWnd, ID_TIMER );
DestroyWindow( hWnd );
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc( hWnd, uMsg, wParam, lParam );
}
void ShowTime( HWND hWnd, HDC hdc, int cxWidth, int cyHight )
{
// Compute Radius,and center point
int radius;
POINT center;
center.x = cxWidth / 2;
center.y = cyHight / 2;
if( cyHight < cxWidth )
radius = cyHight / 2;
else
radius = cxWidth / 2;
// draw dot
HBRUSH hBrush,hOldBrush;
hBrush = (HBRUSH)GetStockObject( BLACK_BRUSH );
hOldBrush = (HBRUSH)SelectObject( hdc, hBrush );
DrawDot( hdc, 360 / 12, 3, radius, center);
DrawDot( hdc, 360 / 60, 1, radius, center);
SelectObject( hdc, hOldBrush );
DeleteObject( hBrush );
// Get local time
SYSTEMTIME systime;
GetLocalTime( &systime );
// draw line
double degree = systime.wSecond * 360 / 60;
HPEN hPen,hOldPen;
hPen = CreatePen( PS_SOLID, 1, RGB(255, 0, 0) );
hOldPen = (HPEN)SelectObject( hdc, hPen );
DrawLine( hdc, degree, 15, radius, center );
SelectObject( hdc, hOldPen );
DeleteObject( hPen );
degree = systime.wMinute * 360 / 60;
hPen = CreatePen( PS_SOLID, 2, NULL );
hOldPen = (HPEN)SelectObject( hdc, hPen );
DrawLine( hdc, degree, 20, radius, center );
SelectObject( hdc, hOldPen );
DeleteObject( hPen );
int hour = systime.wHour;
if (hour >= 12)
{
hour -= 12;
}
degree = hour * 360 / 12 + systime.wMinute / 2;
hPen = CreatePen( PS_SOLID, 3, NULL );
hOldPen = (HPEN)SelectObject( hdc, hPen );
DrawLine( hdc, degree, 30, radius, center );
SelectObject( hdc, hOldPen );
DeleteObject( hPen );
}
void DrawDot( HDC hdc, double degreeInc, int length, LONG radius, POINT center )
{
double degreeNow = 0;
POINT dotNow;
radius -= 10;
while( degreeNow <= 360 )
{
dotNow.x = (LONG)(center.x + radius * sin( degreeNow * PI / 180 ));
dotNow.y = (LONG)(center.y - radius * cos( degreeNow * PI / 180 ));
Ellipse( hdc, dotNow.x - length, dotNow.y - length, dotNow.x + length, dotNow.y + length );
degreeNow += degreeInc;
}
}
void DrawLine( HDC hdc, double degree, int length, LONG radius, POINT center )
{
POINT start, end;
start.x = (LONG)(center.x + 10 * sin( (degree + 180) * PI / 180 ));
start.y = (LONG)(center.y - 10 * cos( (degree + 180) * PI / 180 ));
end.x = (LONG)(center.x + (radius - length) * sin( degree * PI / 180 ));
end.y = (LONG)(center.y - (radius - length) * cos( degree * PI / 180 ));
MoveToEx( hdc, start.x, start.y, NULL );
LineTo( hdc, end.x, end.y );
}