Windows定时器
目录
第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定时器学习
定时器是一个在特定时间或者规则间隔被激发的内核对象.结合定时器的异步程序调用可以允许回调函数在任何定时器被激发的时候执行. 通过调用CreateWaitableTimer()可以创建一个定时器,此函数 ...
- windows定时器编程
目前,Windows下的定时器编程主要有三种方式. 1)SetTimer定时器是利用Windows窗口消息WM_TIMER来实现的.使用方法非常简单,SetTimer创建定时器,KillTimer销毁 ...
- ASP.NET MVC 中应用Windows服务以及Webservice服务开发分布式定时器
ASP.NET MVC 中应用Windows服务以及Webservice服务开发分布式定时器一:闲谈一下:1.现在任务跟踪管理系统已经开发快要结束了,抽一点时间来写一下,想一想自己就有成就感啊!! ...
- 简述System.Windows.Forms.Timer 与System.Timers.Timer用法区别
System.Windows.Forms.Timer 基于窗体应用程序 阻塞同步 单线程 timer中处理时间较长则导致定时误差极大. System.Timers.Timer 基于服务 非阻塞异步 多 ...
- C++实现水波纹、火焰和血浆效果
点击这里查看原文 Code Project着火了! 整个工程有三个类,它们可以让你在图象上添加一些很酷的效果. 我把这些文件都放到我的代码压缩包里面了,并且做了一个小工程来让一些人使用起来更方便,但是 ...
- PHP 函数 ignore_user_abort()详解笔记
定义和用法 ignore_user_abort()函数设置与客户机断开是否会终止脚本的执行 语法 ignore_user_abort(setting) 参数 描述 setting 可选.如果设置为 ...
- Qt之QTimer
简述 QTimer类提供了重复和单次触发信号的定时器. QTimer类为定时器提供了一个高级别的编程接口.很容易使用:首先,创建一个QTimer,连接timeout()信号到适当的槽函数,并调用sta ...
- 修改公司VS_UCOS工程BUG调试过程说明
说明:公司里的工程中,使用VS_UCOS来调试应用程序.业务逻辑.方法是嵌入式和VS分别建一个工程,把底层驱动部分分别添加各自需要的源文件,头文件使用同一个.也就是嵌入式的驱动函数名和参数和VS的函数 ...
- QTimer
目录 简述 详细说明 精度 替代QTimer 成员函数 信号 示例 简述 QTimer类提供了重复和单次触发信号的定时器. QTimer类为定时器提供了一个高级别的编程接口.很容易使用:首先,创建一个 ...
随机推荐
- Android多分辨率适配
前一阶段开发android项目,由于客户要求进行多分辨率适配,能够支持国内主流的分辨率手机.因此经过了几次开发走了很多弯路,目前刚刚领略了android多分辨率适配的一些方法. 先介绍一下所走的弯路, ...
- AJAX和jQuery Ajax总结
AJAX全称为“Asynchronous JavaScript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用,改善用户体验,实现无刷新效果的技术. 使用AJAX的优 ...
- 一、安装JDK和Tomcat
一.安装JDK和Tomcat 1,安装JDK:直接运行jdk-7-windows-i586.exe可执行程序,默认安装即可. 备注:路径可以其他盘符,不建议路径包含中文名及特殊符号. 2.安装Tomc ...
- POJ 1840 Eqs 暴力
Description Consider equations having the following form: a1x13+ a2x23+ a3x33+ a4x43+ a5x53=0 The ...
- number_format
number_format — 以千位分隔符方式格式化一个数字 说明 string number_format ( float $number [, int $decimals = 0 ] ) str ...
- ASP.NET简单登录注册实例
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx. ...
- C#实现ActiveX控件开发与部署
现在,我们手里已经有了这个控件包:QRCode.cab,下面我们编写一个测试的网页 <html> <head> <title>无标题页</title> ...
- acdream1197 Points In Cuboid
题目链接:http://acdream.info/problem?pid=1197 题意:给出一些点.每次给出一个长方体,问在长方体中的点的个数. 思路:kd-tree. const int N=11 ...
- Know How And When To Use System.Message_Level To Control Messages In Oracle Forms
SYSTEM.MESSAGE_LEVEL is used to control the messages that end users see when they use the Oracle For ...
- Beaglebone Black–GPIO 开关 LED(三极管与继电器实验)
上一篇,用 GPIO 直接供电给 LED,用高低电平作开关,不靠谱.GPIO 是信号用的,不是当电源用的.而且,一个 GPIO 只能给可怜的 5mA 左右,取多了会烧(我没烧过不知道是不是真的会烧,但 ...