目录

第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定时器的更多相关文章

  1. Windows定时器学习

    定时器是一个在特定时间或者规则间隔被激发的内核对象.结合定时器的异步程序调用可以允许回调函数在任何定时器被激发的时候执行. 通过调用CreateWaitableTimer()可以创建一个定时器,此函数 ...

  2. windows定时器编程

    目前,Windows下的定时器编程主要有三种方式. 1)SetTimer定时器是利用Windows窗口消息WM_TIMER来实现的.使用方法非常简单,SetTimer创建定时器,KillTimer销毁 ...

  3. ASP.NET MVC 中应用Windows服务以及Webservice服务开发分布式定时器

    ASP.NET MVC 中应用Windows服务以及Webservice服务开发分布式定时器一:闲谈一下:1.现在任务跟踪管理系统已经开发快要结束了,抽一点时间来写一下,想一想自己就有成就感啊!!  ...

  4. 简述System.Windows.Forms.Timer 与System.Timers.Timer用法区别

    System.Windows.Forms.Timer 基于窗体应用程序 阻塞同步 单线程 timer中处理时间较长则导致定时误差极大. System.Timers.Timer 基于服务 非阻塞异步 多 ...

  5. C++实现水波纹、火焰和血浆效果

    点击这里查看原文 Code Project着火了! 整个工程有三个类,它们可以让你在图象上添加一些很酷的效果. 我把这些文件都放到我的代码压缩包里面了,并且做了一个小工程来让一些人使用起来更方便,但是 ...

  6. PHP 函数 ignore_user_abort()详解笔记

     定义和用法 ignore_user_abort()函数设置与客户机断开是否会终止脚本的执行  语法 ignore_user_abort(setting) 参数 描述 setting 可选.如果设置为 ...

  7. Qt之QTimer

    简述 QTimer类提供了重复和单次触发信号的定时器. QTimer类为定时器提供了一个高级别的编程接口.很容易使用:首先,创建一个QTimer,连接timeout()信号到适当的槽函数,并调用sta ...

  8. 修改公司VS_UCOS工程BUG调试过程说明

    说明:公司里的工程中,使用VS_UCOS来调试应用程序.业务逻辑.方法是嵌入式和VS分别建一个工程,把底层驱动部分分别添加各自需要的源文件,头文件使用同一个.也就是嵌入式的驱动函数名和参数和VS的函数 ...

  9. QTimer

    目录 简述 详细说明 精度 替代QTimer 成员函数 信号 示例 简述 QTimer类提供了重复和单次触发信号的定时器. QTimer类为定时器提供了一个高级别的编程接口.很容易使用:首先,创建一个 ...

随机推荐

  1. css公共样式,初始化

    /* CSS Document */ body, button, select, textarea, input, label, option, fieldset, legend{font-famil ...

  2. android 介绍0

    Android  (src  res  maifest) src ==>pacege==>类(后台代码) layout ==>界面 value ==>字符串 R类:layout ...

  3. 关于python的__name__理解

    Python中,每个模块有个__name__属性,当模块是在自己文件下执行的,那么它的__name__属性是__main__,而当它被引入到别的模块中,那么在别的模块下(import模块名 可以引入一 ...

  4. [cdoj1380] Xiper的奇妙历险(3) (八数码问题 bfs + 预处理)

    快要NOIP 2016 了,现在已经停课集训了.计划用10天来复习以前学习过的所有内容.首先就是搜索. 八数码是一道很经典的搜索题,普通的bfs就可求出.为了优化效率,我曾经用过康托展开来优化空间,甚 ...

  5. Linux下新的网络管理工具ip替代ifconfig零压力

    如果你使用 Linux 足够久,那么你自然知道一些工具的来与去.2009年 Debian 开发者邮件列表宣布放弃使用缺乏维护的 net-tools 工具包正是如此.到今天 net-tools 仍然被部 ...

  6. 优化Linux生产服务器的经验之谈

    [51CTO独家特稿]如何优化自己的Linux生产服务器?本文结合实际的工作经验,总结了优化Linux生产服务器的九大要点.如果有些方法您尚未采用,不妨一试. 一.时间同步 生产环境下的服务器对时间的 ...

  7. git-github-svn你们都是个什么东东

    Git 和 GitHub 有什么区别  百科中是这样说的 Git是一款免费.开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目. GitHub 是一个面向开源及私有软件项目的托管平台,因为 ...

  8. 2013年5月~2013年11月份(转接关于ns51服务平台项目)相关资料:

    <1> [平台首页] 界面截图:(网络游客所看到的界面首页) <2>[注册] 有需求则注册会员(略...) <3>[个人空间] 注册成功后进入个人空间(有深层次的需 ...

  9. iOS - OC 数据持久化

    1.Sandbox 沙箱 iOS 为每个应用提供了独立的文件空间,一个应用只能直接访问为本应用分配的文件目录,不可以访问其他目录,每个应用自己独立的访问空间被称为该应用的沙盒.也就是说,一个应用与文件 ...

  10. main函数中argc理解

    其实: int main(int argc,char *argv[])是UNIX和Linux中的标准写法,而int main()只是UNIX及Linux默许的用法..void main(int arg ...