如果程序在整个运行过程中需要一个计时器,在WinMain函数中或窗口过程处理WM_CREATE 消息时,调用SetTimer函数创建一个计时器。在离开WinMain函数时或是处理WM_DESTROY消息时,调用KillTimer函数销毁计时器。基于调用SetTimer参数的不同,可采取三种不同的方法使用计时器。
本节必须掌握的知识点:
第43练:使用计时器方法一
第44练:使用计时器方法二
使用计时器方法三
7.2.1 第43练:使用计时器方法一
/*------------------------------------------------------------------
043 WIN32 API 每日一练
第43个例子BEEPER1.C:使用计时器方法一
WM_TIMER消息
SetTimer函数:WM_CREATE时创建计时器
KillTimer函数:WM_DESTORY时销毁
(c) www.bcdaren.com, 2020
----------------------------------------------------------------*/
#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");
…(略)
return msg.wParam;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)
{
static BOOL fFlipFlop = FALSE;//标记值
HBRUSH hBrush;
PAINTSTRUCT ps;
RECT rect;
HDC hdc;
switch (message)
{
case WM_CREATE:
//创建计时器,间隔1秒发送一次WM_TIMER
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);
//选择画刷
hBrush = CreateSolidBrush(fFlipFlop ? RGB(0,0,255) : RGB(255,0,0));
GetClientRect(hwnd,&rect);
//绘制并填充矩形
FillRect(hdc,&rect,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);
}
/******************************************************************************
WM_TIMER消息:计时器到期时,将其发布到安装线程的消息队列中
wParam [输入]计时器标识符。
lParam [输入]指向应用程序定义的回调函数的指针,该指针在安装计时器时传递给SetTimer函数。
*******************************************************************************
SetTimer函数:用指定的超时值创建一个计时器
UINT_PTR SetTimer(
HWND hWnd, //与计时器关联的窗口的句柄
UINT_PTR nIDEvent,//非零计时器标识符
UINT uElapse,//超时值--间隔时间,以毫秒为单位。
TIMERPROC lpTimerFunc//当超时值过去时,指向要通知的回调函数指针
);
*******************************************************************************
KillTimer函数:销毁指定的计时器
BOOL KillTimer(
HWND hWnd,
UINT_PTR uIDEvent//计时器ID
);
*/
运行结果:
图7-1 使用计时器方法一
总结
- 实例BEEPER1.C的实现非常简单。首先窗口过程在处理WM_CREATE消息时,调用SetTimer函数创建了一个计时器:
SetTimer(hwnd,ID_TIMER,1000,NULL);
参数ID_TIMER为计时器ID。参数1000以毫秒为单位,1000毫秒等于1秒,即每间隔1秒向窗口消息队列发送一次WM_TIMER消息。最后一个参数为计时器回调函数,NULL表示未设置回调函数。这是使用计时器方法一的典型特征,通过处理WM_TIMER消息响应计时器,而不是使用计时器回调函数。
2.接着处理WM_TIMER消息时,响铃的同时,将标志值fFlipFlop取反,然后调用InvalidateRect函数重绘窗口客户区。
3.处理WM_PAINT消息时,创建一个蓝红不断切换的画刷(每接收到一次WM_TIMER消息切换一次,即大约每秒切换一次),填充矩形。
4.处理WM_DESTROY消息时,调用KillTimer函数销毁计时器:
KillTimer(hwnd,ID_TIMER);
7.2.2 第44练:使用计时器方法二
/*------------------------------------------------------------------
044.C -- A daily practice
第44个例子BEEPER2.C:使用计时器方法二
CALLBACK TimerProc 回调函数
(c) www.bcdaren.com, 2020
----------------------------------------------------------------*/
#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 TCHAR szAppName[] = TEXT("Beeper2");
…(略)
return msg.wParam;
}
//计时器回调函数
void CALLBACK TimerProc (HWND hwnd, UINT message, UINT iTimerID,DWORD dwTime)
{
static BOOL fFlipFlop = FALSE;
HBRUSH hBrush;
RECT rect;
HDC hdc;
MessageBeep(-1);//响铃
fFlipFlop = !fFlipFlop;//取反
GetClientRect(hwnd, &rect);//获取客户区矩形参数
hdc = GetDC(hwnd);
//创建红色或蓝色画刷
hBrush = CreateSolidBrush(fFlipFlop ? RGB(0, 0, 255) : RGB(255, 0, 0));
FillRect(hdc, &rect, hBrush);//填充矩形框
ReleaseDC(hwnd,hdc);//释放DC
DeleteObject(hBrush);//删除画刷
}
//窗口过程
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
//创建计时器,TimerProc为计时器回调函数
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);
}
/******************************************************************************
CALLBACK TimerProc 回调函数
函数定义
void CALLBACK TimerProc (HWND hwnd, UINT message, UINT iTimerID,DWORD dwTime)
hwnd参数:调用SetTimer时指定的窗口句柄。
UINT message参数:Windows只发送 WM_TIMER消息到TimeerProc,所以消息参数总是WM_TIMER。
UINT iTimerID参数:计时器的 ID。
DWORD dwTime:从GetTickCount函数返回的值,它记录了自从Windows启动到现在所逝去的毫秒数,最长为49.7天。
函数调用
SetTimer(hwnd,ID_TIMER,1000,TimerProc);//创建计时器
SetTimer函数的第四个参数必须设定为回调函数的地址
*/
运行结果:
图7-2 使用计时器方法二
总结
计时器回调函数是在计时器事件发生时被调用的函数,用于执行特定的操作或触发相应的事件。
实例BEEPER2.C的窗口过程在处理WM_CREATE消息时,调用SetTimer函数创建计时器的前三个参数与实例BEEPER1.C完全相同,只是最后一个参数计时器的回调函数不再是NULL,而是TimerProc。
SetTimer(hwnd,ID_TIMER,1000,TimerProc);
与此相应,窗口过程不再需要处理WM_TIMER消息。窗口过程对WM_TIMER消息和WM_PAINT消息的处理放到了计时器回调函数TimerProc中,仅此而已。
7.2.3 使用计时器方法三
第三种设置计时器的方法与第二种方法相似,只不过SetTimer的hwnd参数被设置为 NULL,而且第二个参数(正常情况下是计时器的ID)也被忽略了。此外,这个函数会返回计时器的ID:
iTimerlD = SetTimer (NULL, 0, wMsecInterval, TimerProc);
如果SetTimer函数返回的iTimerlD为0,表示没有可用的计时器,这样的情况是极罕见的。
传给KillTimer的第一个参数(通常是窗口的句柄)也必须是NULL。计时器的ID必须是从SetTimer返回的值:
KillTimer (NULL, iTimerlD);
传给TimerProc计时器函数的hwnd参数也将是NULL。这种设置计时器的方法很少用 到。如果在程序中,需要在不同的时刻调用很多次SetTimer,但又不想记录哪些计时器ID 已经被使用过,那么这种方法可能会派上用场。