使用计时器只需要了解两个函数:CWnd::SetTimer用于产生以指定时间间隔发送消息的计时器;CWnd::KillTimer消除计时器。
根据传递给SetTimer的参数,计时器通过下面的两种途径通知应用程序间隔时间已到:给指定窗口发送WM_TIMER消息;调用一个应用程序定义的回调函数。这两种类型的计时器消息在发送给应用程序时具有较低的优先级别,只有在消息队列中没有其他消息时才处理它们。
计时器消息永远不会积压在消息队列中。如果将计时器设置为每100毫秒产生一个消息,而在整整1秒内应用程序都忙于处理其他消息了,当消息队列清空以后它并不会突然接收到10个快速产生的消息。相反,它只接受一条。但是windows应用程序也决不应该用大量时间处理一个消息。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
设置计时器方法1:用计时器ID和计时器时间间隔调用调用SetTimer,然后将WM_TIMER消息映射给OnTimer函数。
①如果使用两个以上的计时器的应用程序,会使用计时器ID来标识产生特定消息的计时器;
时间间隔指定了以毫秒为单位连续两次WM_TIMER消息之间的时间间隔,有效值从1ms到(2^23-1)ms,大概是49天半。
语句:SetTimer( 1, 500, NULL);//分配一个计时器,ID为1,每500ms给窗口发送一个WM_TIMER消息,NULL将计时器配置为发送WM_TIMER消息而不是调用回调函数。
PS:大多数系统(特别是基于Intel的),Windows计时器基于的硬件计时器或多或少每54.9ms走一下,实际上,Windows要将传递给SetTimer的值四舍五入到下一个55ms。故SetTimer(1,1,NULL)和SetTimer(1,50,NULL)效果相同
如果函数执行成功,SetTimer返回的值为计时器ID,否则为0。(如果参数中指定ID是0,返回值ID为1)
还可以用SetTimer改动从前分配的已用完的时间值,如果计时器1已经存在,语句SetTimer( 1,1000,NULL);将重新给它安排时间间隔1000ms。
②MFC的ON_WM_TIMER消息映射表宏将WM_TIMER消息映射给了类成员函数OnTimer。OnTimer的原型:afx_msgvoid OnTimer( UINT nTimerID)
可以在OnTimer中做能够在其他消息处理函数中所作的任何事情。
PS:对于其他消息而言,WM_TIMER消息不是被异步处理的。
设置计时器方法2:配置计时器在应用程序中调用一个回调函数而不是发送WM_TIMER消息。通常用在使用了多个计时器的应用程序中,使得可以给每个计时器都分配唯一的处理函数。
在调用::DispatchMessage之前,处理计时器回调函数和计时器消息的处理过程是相同的。两者的差别最多就是:回调函数的开销要稍微小一点,不涉及消息映射和窗口处理。
计时器开始运行后,Windows会在消息队列中设置一个标志,标识计时器消息或回调函数是否在等待处理(开/关特性就说明为什么计时器消息不会在消息队列中堆积)。
语句:SetTimer( ID_TIMER, 100, TimerProc); //TimerProc指定回调函数的名字
回调函数原型:
void CALLBACK TimerProc( HWND hWnd, //窗口句柄
UINT nMsg, //消息ID
UINT nTimerID, //计时器 ID
DWORD dwTime ); //从Windows启动以后经过时间的ms数
CALLBACK为回调函数的关键字
PS:回调函数应该是一个静态成员函数或全局函数
静态成员函数不接受this指针,因此不能访问自己类中的非静态成员。//?
回调函数为静态原因:由于回调函数在C++中要求专门处理,Windows严格定义了回调函数接口,即通过参数列表传递参数,当C++类的成员函数被声明时,编译程序会自动附加一个额外的参数来保存this指针。附加了参数就使回调函数参数列表与Windows期望的参数列表不匹配了,甚至包括无效内存访问错误。而将回调函数声明为静态成员函数是最简单直接的一种。
OnIdle:
从CWinApp继承而来,原型:virtual BOOL OnIdle( LONG lCount )
lCount是32位的值,指定了从上次消息处理以来OnIdle被调用的总次数。
当lCount等于0和1时是主框架在执行它的处理。