Windows定时器
来源:互联网 发布:中国网络喷子 编辑:程序博客网 时间:2024/06/16 20:39
目录
第1章定时器 1
1.1 创建定时器 1
1.2 销毁定时器 1
1.3 定时器的运作 1
1.3.1 产生WM_TIMER消息 1
1.3.2 分发WM_TIMER消息 2
1.4 WM_TIMER 消息的重入 3
第1章定时器
1.1 创建定时器
请使用API函数 SetTimer 来创建定时器,其原型如下:
UINT SetTimer(HWND hWnd,UINT nIDEvent,UINT uElapse,TIMERPROC lpTimerFunc);
有这么两种用法
1、SetTimer(hWnd,nID,uElapse,NULL);定时给窗口 hWnd 寄送(PostMessage) WM_TIMER 消息;
2、SetTimer(hWnd,nID,uElapse,TimerProc); 不论 hWnd 是否为 NULL,定时调用 TimerProc 函数。
1.2 销毁定时器
销毁定时器请使用KillTimer函数,其原型如下:
BOOL KillTimer(HWND hWnd,UINT uIDEvent);
第1个参数应与SetTimer的第1个参数保持一致;
第2个参数:如果SetTimer的第1个参数是一个有效的窗口句柄,则此参数应与SetTimer的第2个参数保持一致。否则此参数应为SetTimer的返回值。
1.3 定时器的运作
不论 SetTimer(hWnd,nID,uElapse,NULL) 还是 SetTimer(NULL,nID,uElapse,TimerProc),其实质都是处理WM_TIMER消息。
1.3.1 产生WM_TIMER消息
WM_TIMER消息并不是 Windows 系统定时、自动增加到消息队列的,而是调用GetMessage或PeekMessage的时候,才会产生WM_TIMER消息。请参考如下代码:
void CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime)
{
}
UINT TestTimer()
{
MSG msg;
UINT nTimer = SetTimer(NULL,100,1000,TimerProc);
Sleep(3050);
TRACE(_T("Tick=%d\n"),GetTickCount());
PeekMessage(&msg,NULL,0,0,PM_NOREMOVE);
Sleep(1050);
TRACE(_T("Tick=%d\n"),GetTickCount());
PeekMessage(&msg,NULL,0,0,PM_NOREMOVE);
while(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if(msg.message == WM_TIMER)
{
TRACE(_T("Timer=%d\n"),msg.time);
}
}
KillTimer(NULL,nTimer);
return 0;
}
在Windows XP下,运行结果为:
Tick=7356593
Tick=7357656
Timer=7356593
Timer=7357656
虽然两次Sleep的时间合计有4秒多,但消息队列中WM_TIMER的个数并不是4个而是2个。而且这两个WM_TIMER的时刻与两次GetTickCount的时刻完全相等。合理的解释是:在调用PeekMessage(&msg,NULL,0,0,PM_NOREMOVE);时,WM_TIMER消息才被创建并增加到消息队列。如果调用GetMessage或PeekMessage(&msg,NULL,0,0,PM_REMOVE);则创建的WM_TIMER消息不会被增加到消息队列。
1.3.2 分发WM_TIMER消息
通过GetMessage或PeekMessage获得消息之后,一般会调用TranslateMessage和DispatchMessage 进行消息处理。
TranslateMessage对 WM_TIMER 消息不做任何处理。
DispatchMessage(&msg) 负责分发 WM_TIMER 消息,其处理逻辑如下:
if(msg.lParam)
{//如果SetTimer的第4个参数不为NULL,则调用这个回调函数
TIMERPROC pfn = (TIMERPROC)msg.lParam;
pfn(msg.hwnd,WM_TIMER,msg.wParam,msg.time);
}
else
{//交给窗口过程去处理
WNDPROC pfn = (WNDPROC)GetWindowLong(msg.hwnd,GWL_WNDPROC);
CallWindowProc(pfn,msg.hwnd,WM_TIMER,msg.wParam,msg.lParam);
}
也就是说:如果SetTimer的第4个参数不为 NULL,则第1个参数所指定的 hwnd 将无法接收、处理 WM_TIMER 消息。
1.4 WM_TIMER 消息的重入
所谓重入就是当前的消息还没有处理完毕就进入下一个消息的处理。因为WM_TIMER消息是入队消息,所以一般情况下,对WM_TIMER的处理是不会重入的。但也有特殊情况,请参考如下代码:
//定义定时器处理函数
void CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime)
{
TRACE(_T("进入 OnTimer=%lu\n"),dwTime);
Sleep(3500);
MSG msg;
while(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(1000);
TRACE(_T("离开 OnTimer=%lu\n"),dwTime);
}
//设置定时器
SetTimer(100,1000,TimerProc);
本来TimerProc一秒被调用一次,现在情况发生了变化:在TimerProc内部,Sleep(3500)后再调用PeekMessage会立即产生WM_TIMER消息。DispatchMessage会再次调用TimerProc函数处理这个消息。结果就是TimerProc函数无限制的递归调用自己,永远不会返回,最终会因为栈空间溢出而导致程序异常退出。
为了防止TimerProc函数的重入并可能引起的程序崩溃,就需要阻止重入TimerProc函数。可行的方法之一如下:
void CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime)
{
static bool bWorking = false; //是否正在处理 WM_TIMER 消息
if(bWorking)
{//如果正在处理 WM_TIMER 消息则返回,这样就防止了重入
return;
}
bWorking = true; //标记正在处理 WM_TIMER 消息
... ... ... //处理 WM_TIMER 消息
bWorking = false; //标记 WM_TIMER 消息已经处理完毕
}
- windows定时器
- Windows定时器
- Windows定时器
- windows定时器详解,如何使用windows定时器
- windows定时器---Win32
- Windows定时器管理
- Windows Timer定时器使用方法
- Windows定时器的使用
- Windows定时器学习
- windows服务不支持定时器
- 第六篇 Windows定时器
- windows中的定时器
- 高精度定时器(Windows)
- windows定时器设置
- Windows控制台的定时器
- Windows中的定时器
- windows定时器的总结
- Windows定时器SetTimer
- 进程与线程
- LayaAir 打包 App(测试版)
- android ViewPropertyAnimator
- Git使用记录
- QString的相关类型转换
- Windows定时器
- 配置tomcat,使得一台主机运行多个tomcat容器
- [LeetCode]Move Zeroes
- 优秀开源地址记录(亲测)
- 淘宝网店应该怎么样去做好宝贝SEO优化?
- java中的乱码问题
- 移动表空间内数据两种方式
- 关于多函数选择触发的方法
- 智能优化-遗传算法(3)