Windows定时器的使用

来源:互联网 发布:北京中信网络 编辑:程序博客网 时间:2024/06/05 06:11
//本文借鉴万立中老师的一篇博客,增加了些注释,感觉是很好的sdk编程的案例之一。
 
#include <windows.h>#include <time.h>#include <iostream>using namespace std; int bX, bY;//记录方块移动的坐标(方块左上角坐标)int b_Speed;//方块移动的速度(一次移动的数目)RECT WinRect;//窗口的大小//声明回调函数LONG CALLBACK MyWndProc(HWND, UINT, WPARAM, LPARAM);//========================================================//WinMain函数//========================================================int APIENTRY WinMain(HINSTANCE hInstance,                                        HINSTANCE hPrevInstance,                                        LPSTR lpCmdLine, int nCmdShow) {       MSG msg;       HWND hWnd;       WNDCLASSEX wnd;        wnd.cbSize                   = sizeof(WNDCLASSEX);        wnd.style                      = CS_HREDRAW | CS_VREDRAW;       wnd.lpfnWndProc         = (WNDPROC)MyWndProc;       wnd.cbClsExtra             = 0;       wnd.cbWndExtra          = 0;       wnd.hInstance               = hInstance;       wnd.hIcon                     = NULL;       wnd.hCursor                 = LoadCursor(NULL, IDC_ARROW);       wnd.hbrBackground      = (HBRUSH)(COLOR_WINDOW+1);       wnd.lpszMenuName      = NULL;       wnd.lpszClassName     = "TimerApp";       wnd.hIconSm               = NULL;        RegisterClassEx(&wnd);        hWnd = CreateWindow( "TimerApp", "Timer测试程序",                                                WS_OVERLAPPEDWINDOW,                                               CW_USEDEFAULT, 0, 690, 520,                                                NULL, NULL, hInstance, NULL);       if (!hWnd) return FALSE;        ShowWindow(hWnd, nCmdShow);       UpdateWindow(hWnd);        while (GetMessage(&msg, NULL, 0, 0))        {              TranslateMessage(&msg);              DispatchMessage(&msg);       }       return msg.wParam;}//========================================================//回调函数//========================================================   LONG CALLBACK MyWndProc (HWND hWnd, UINT message,                                                             WPARAM wParam, LPARAM lParam){       HDC hdc;       HGDIOBJ mbrush, oldbrush;//定义画刷句柄       int r;//为下面保存随机数设定变量       GetClientRect(hWnd, &WinRect);//获取窗口的大小,从而为下面的方块坐标设定范围        switch (message) {        case WM_CREATE:              srand((unsigned int)time(NULL));              bX = (WinRect.right-100)/2;//方块的初始化坐标              bY = (WinRect.bottom-100)/2;              b_Speed = 5;              //设置定时器,间隔时间为10毫秒              SetTimer(hWnd, 1, 10, (TIMERPROC) NULL);              return 0;        case WM_TIMER:              r = rand()%2;//随机数取余数              if(r > 0) //(因为可能会有随机负数,大于0则x移动,否则y轴移动)                     bX = bX + b_Speed;              else                     bY = bY + b_Speed;               if(bX>WinRect.right-100 || bY>WinRect.bottom-100 || bX<0 || bY<0)               {//下面是为了使方块不发越界                     if(bX>WinRect.right-100) bX = WinRect.right-100;                     if(bY>WinRect.bottom-100) bY = WinRect.bottom-100;                     if(bX<0) bX = 0;                     if(bY<0) bY = 0;                      b_Speed = -b_Speed;//当方块碰到边框后,让方块向相反的方向移动              }              //调用WM_PAINT重绘(并且擦除背景)              InvalidateRect(hWnd, &WinRect, TRUE);                return 0;        case WM_PAINT:              PAINTSTRUCT ps;              hdc = BeginPaint(hWnd, &ps);              mbrush = CreateSolidBrush(RGB(255, 0, 0));              oldbrush = SelectObject(hdc, mbrush);              WinRect.left = bX;              WinRect.right = WinRect.left + 100;              WinRect.top = bY;              WinRect.bottom = WinRect.top+100;              FillRect(hdc, &WinRect, (HBRUSH)mbrush);//填充矩形方块              SelectObject(hdc, oldbrush);              DeleteObject(mbrush);              EndPaint(hWnd,&ps);              return 0;        case WM_CLOSE:              if(IDOK==MessageBox(NULL,"你确定要退出吗?",                      "提示", MB_OKCANCEL|MB_ICONQUESTION))              {                     DestroyWindow(hWnd);              }              return 0;        case WM_DESTROY:              PostQuitMessage(0);              return 0;              }       return DefWindowProc(hWnd, message, wParam, lParam);}

定时器在实际编程中使用频率比较高,例如一些需要间隔一定时间自动执行的任务,如果任务执行对时间精度要求不是太苛求,使用简单的定时器就是一个较好的选择。当然,由于定时器在系统的优先级较低,有时在执行具体任务时可能会遇到一些意想不到的问题。如果这样的话,可以考虑直接使用线程或调用GetTickCount函数自己处理。对于一些简单的游戏编程,定时器完全可以胜任。本文以VC6.0为环境说明定时器的使用方法。

在Windows编程中,可以使用SetTimer函数设置并启动定时器。该函数在SDK的API定义中有四个参数,原型定义如下:

UINT SetTimer( HWND hWnd,

              UINT nIDEvent,

              UINT uElapse,

              TIMERPROC lpTimerFunc );

       参数含义:

n hWnd

与定时器关联的窗口句柄。该窗口必须被调用的线程所拥有。如果该参数设为NULL,则意味着定时器没有需要关联的窗口,那么第二个参数nIDEvent就会被忽略;

n nIDEvent

指定一个非0的标识。如果hWnd参数为NULL,该参数被忽略;

n uElapse

定时器间隔的时间,以毫秒为单位;

n lpTimerFunc

指定的间隔时间自动执行的回调函数的指针;如果该参数为NULL,系统则自动向英勇程序队列发送WM_TIMER 消息;如果不为NULL,则会自动执行所指定的回调函数。该回调函数的定义如下:

void CALLBACK TimerProc(HWND hwnd,

                          UINT uMsg,

                          UINT idEvent,

                          DWORD dwTime );

回调函数的参数含义:

l hwnd:   要与定时器关联的窗口句柄;

l uMsg:   指定WM_TIMER消息;

l idEvent:定时器的标识;

l dwTime:指定自系统启动后的已经过去的毫秒数,该值由GetTickCount函数获得。

从以上SetTimer函数的定义可以看出,使用定时器时,有两个地方可以用来编写定时器要执行的任务代码:(1)WM _TIMER消息中;(2)自定义的TIMERPROC类型的任务函数。

  为了说明问题,我们就在窗口实现一个随机移动的方块作为我们的定制绘制任务。下面分别对SetTimer函数的两种使用方式进行举例说明。

(1) 在WM _TIMER消息中编写任务代码:

首先,由于要用到随机函数以及时间函数,需要在以上代码的include部分增加下面的引用及变量的定义,具体作用见代码注释:

#include <time.h>

#include <iostream>

using namespace std;

 

int bX, bY;//记录方块移动的坐标

int b_Speed;//方块移动的速度

RECT WinRect;//窗口的大小

       接下来,在MyWndProc回调函数中增加WM_CREATE、WM_TIMER消息,其中在WM_CREATE消息中调用SetTimer函数并初始化,需要注意的是,SetTimer函数的第三个参数需要设置为NULL,然后在WM_TIMER消息中改变方块的坐标位置、检测边界并调用InvalidateRect函数发送重绘消息,最后在WM_PAINT消息中编写具体的绘制代码。对修改后的代码重新进行编译,然后执行代码,会在窗口中看到一个不断随机移动的红色方块,当方块达到边界后会返回朝相反的方向移动。

我们知道,sdk的方式是调用函数,所以我们可以把处理定时器的一部分封装成函数,如下:

 

void CALLBACK TimerFunc(HWND hWnd,UINT nMsg,UINT

                                              nTimerid,DWORD dwTime)

{

       GetClientRect(hWnd, &WinRect);

       int r = rand()%2;

       if(r > 0)

              bX = bX + b_Speed;

       else

              bY = bY + b_Speed;

       if(bX>WinRect.right-100 || bY>WinRect.bottom-100 || bX<0 || bY<0)

       {

              if(bX>WinRect.right-100) bX = WinRect.right-100;

              if(bY>WinRect.bottom-100) bY = WinRect.bottom-100;

              if(bX<0) bX = 0;

              if(bY<0) bY = 0;

              b_Speed = -b_Speed;

       }

       //调用WM_PAINT重绘(并且擦除背景)

       InvalidateRect(hWnd, &WinRect, TRUE); 

}

 

 

最后注意将SetTimer函数的最后一个参数修改为函数入口的地址,如下:

SetTimer(hWnd, 1, 10, (TIMERPROC)TimerFunc);

 

改变后的代码如下:

 

#include <windows.h>#include <time.h>#include <iostream>using namespace std; int bX, bY;//记录方块移动的坐标(方块左上角坐标)int b_Speed;//方块移动的速度(一次移动的数目)RECT WinRect;//窗口的大小//声明回调函数LONG CALLBACK MyWndProc(HWND, UINT, WPARAM, LPARAM);void CALLBACK TimerFunc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime);//========================================================//WinMain函数//========================================================int APIENTRY WinMain(HINSTANCE hInstance,                                        HINSTANCE hPrevInstance,                                        LPSTR lpCmdLine, int nCmdShow) {       MSG msg;       HWND hWnd;       WNDCLASSEX wnd;        wnd.cbSize                   = sizeof(WNDCLASSEX);        wnd.style                      = CS_HREDRAW | CS_VREDRAW;       wnd.lpfnWndProc         = (WNDPROC)MyWndProc;       wnd.cbClsExtra             = 0;       wnd.cbWndExtra          = 0;       wnd.hInstance               = hInstance;       wnd.hIcon                     = NULL;       wnd.hCursor                 = LoadCursor(NULL, IDC_ARROW);       wnd.hbrBackground      = (HBRUSH)(COLOR_WINDOW+1);       wnd.lpszMenuName      = NULL;       wnd.lpszClassName     = "TimerApp";       wnd.hIconSm               = NULL;        RegisterClassEx(&wnd);        hWnd = CreateWindow( "TimerApp", "Timer测试程序",                                                WS_OVERLAPPEDWINDOW,                                               CW_USEDEFAULT, 0, 690, 520,                                                NULL, NULL, hInstance, NULL);       if (!hWnd) return FALSE;        ShowWindow(hWnd, nCmdShow);       UpdateWindow(hWnd);        while (GetMessage(&msg, NULL, 0, 0))        {              TranslateMessage(&msg);              DispatchMessage(&msg);       }       return msg.wParam;}//========================================================//回调函数//========================================================   LONG CALLBACK MyWndProc (HWND hWnd, UINT message,                                                             WPARAM wParam, LPARAM lParam){       HDC hdc;       HGDIOBJ mbrush, oldbrush;//定义画刷句柄       int r;//为下面保存随机数设定变量       GetClientRect(hWnd, &WinRect);//获取窗口的大小,从而为下面的方块坐标设定范围        switch (message) {        case WM_CREATE:              srand((unsigned int)time(NULL));              bX = (WinRect.right-100)/2;//方块的初始化坐标              bY = (WinRect.bottom-100)/2;              b_Speed = 5;              //设置定时器,间隔时间为10毫秒           SetTimer(hWnd, 1, 10, (TIMERPROC)TimerFunc);              return 0;        case WM_PAINT:              PAINTSTRUCT ps;              hdc = BeginPaint(hWnd, &ps);              mbrush = CreateSolidBrush(RGB(255, 0, 0));              oldbrush = SelectObject(hdc, mbrush);              WinRect.left = bX;              WinRect.right = WinRect.left + 100;              WinRect.top = bY;              WinRect.bottom = WinRect.top+100;              FillRect(hdc, &WinRect, (HBRUSH)mbrush);//填充矩形方块              SelectObject(hdc, oldbrush);              DeleteObject(mbrush);              EndPaint(hWnd,&ps);              return 0;        case WM_CLOSE:              if(IDOK==MessageBox(NULL,"你确定要退出吗?",                      "提示", MB_OKCANCEL|MB_ICONQUESTION))              {                     DestroyWindow(hWnd);              }              return 0;        case WM_DESTROY:              PostQuitMessage(0);              return 0;              }       return DefWindowProc(hWnd, message, wParam, lParam);}void CALLBACK TimerFunc(HWND hWnd,UINT nMsg,UINT                                               nTimerid,DWORD dwTime){       GetClientRect(hWnd, &WinRect);       int r = rand()%2;       if(r > 0)               bX = bX + b_Speed;       else              bY = bY + b_Speed;       if(bX>WinRect.right-100 || bY>WinRect.bottom-100 || bX<0 || bY<0)        {              if(bX>WinRect.right-100) bX = WinRect.right-100;              if(bY>WinRect.bottom-100) bY = WinRect.bottom-100;              if(bX<0) bX = 0;              if(bY<0) bY = 0;              b_Speed = -b_Speed;       }       //调用WM_PAINT重绘(并且擦除背景)       InvalidateRect(hWnd, &WinRect, TRUE);  }