Windows定时器

目录

第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 消息已经处理完毕

}

时间: 2024-12-14 21:31:29

Windows定时器的相关文章

Windows定时器学习

定时器是一个在特定时间或者规则间隔被激发的内核对象.结合定时器的异步程序调用可以允许回调函数在任何定时器被激发的时候执行. 通过调用CreateWaitableTimer()可以创建一个定时器,此函数返回一个指向内核对象的句柄.若定时器已经存在,你可以通过使用OpenWaitableTimer()获得一个进程相关的句柄.无论是通过CreateWaitableTimer() 还是通过OpenWaitableTimer()获得的句柄,在不需要定时器时必须释放,方法是使用函数CloseHandle()

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

ASP.NET MVC 中应用Windows服务以及Webservice服务开发分布式定时器一:闲谈一下:1.现在任务跟踪管理系统已经开发快要结束了,抽一点时间来写一下,想一想自己就有成就感啊!!  2.关于任务跟踪管理系统项目中遇到的Windows服务以及Webservice的综合应用的问题. 大家好这是我第二次写博客 ,写的不好请大家多多谅解, 希望大家可以多多指正. 二:我稍微的整理了一下关于这个分布式定时器需求:1.根据任务跟踪管理系统中的数据库的AnswerSheet 表格中找到客户编

Windows核心编程之创建可等待定时器及其APC回调

概述 创建可等待定时器是Windows内部线程同步的方式之一,本文简单讲述如何使用这一内核对象进行线程同步. 使用方法 创建对象: //创建事件内核对象,默认未触发状态 HANDLE hTimer = CreateWaitableTimer(NULL, TRUE, NULL); 设置对象属性: CreateWaitableTimer创建完成后内核对象处于未触发状态,需要使用API BOOL WINAPI SetWaitableTimer( __in HANDLE hTimer, __in con

定时器(api)

定时器 壹佰软件开发小组  整理编译   Microsoft Windows定时器是一种输入设备,它周期性地在每经过一个指定的时间间隔后就通知应用程序一次.您的程序将时间间隔告诉Windows,例如「每10秒钟通知我一声」,然后Windows给您的程序发送周期性发生的WM_TIMER消息以表示时间到了. 初看之下,Windows定时器似乎不如键盘和鼠标设备重要,而且对许多应用程序来说确实如此.但是,定时器比您可能认为的要重要得多,它不只用于计时程序,比如出现在工具列中的Windows时钟和这一章

Cpp_with_MFC官方实例----定时器的使用

本文使用的实例是位于程序安装目录的prj文件夹下Cpp_with_MFC项目,该项目包括两个工程,分别是Cpp_with_MFC工程和kernel工程 kernel工程主要有两个函数组成: 1. _initFunction(void* pArgs) ,该函数是用于kernel工程生成的dll文件被外部调用时的初始化代码,该函数内部没有实质内容. 2. _timerCallBack(void* pArgs, void* pContext),该函数为定时器回调函数,每进入一次则计数值+1,每达到50

wxWidgets第十四课 wxTimer定时器

说明 OnIdle CPU空闲的情况下处理消息,如果需要定时功能,就需要使用定时器wxTimer 问题 比如定时器函数运行耗时10秒,定时的时间是10毫秒,是否是每隔10毫秒执行一次定时器函数,还是等待定时器函数运行结束,才开始重新计时 结果 在定时器函数中执行::Sleep(10000);等待10秒,发现实际上需要等待定时器函数执行结束,才开始重新计时.所以在如下的场景需要特别小心:需要定时读取数据,而读取数据之后,执行一大堆耗时的操作,这个时候,就需要启动线程去处理,而不能在定时器函数中完成

高精确度且线程分离的定时器——多媒体定时器

说道定时器,很多人都会想到Windows定时器SetTimer吧!其实,项目里面原本确实是使用这种方法实现动画效果的,但是后来问题出现了!由于WM_TIMER消息优先级比较低,常常被丢失,导致一个WM_MOUSEMOVE消息都会影响整个动画的效果. 这时我就考虑,是否能给定时器创建一个单独的线程,接着就发现了"多媒体定时器"这个东西.它是一个高精确度定时器,一般的Windows定时器只能精确到55ms,而多媒体定时器能精确到10ms内.同时,在启动一个多媒体定时器的同时,会自动创建一个

Windows API参考大全新编

书名:新编Windows API参考大全 作者:本书编写组 页数:981页 开数:16开 字数:2392千字 出版日期:2000年4月第二次印刷 出版社:电子工业出版社 书号:ISBN 7-5053-5777-8 定价:98.00元 内容简介 作为Microsoft 32位平台的应用程序编程接口,Win32 API是从事Windows应用程序开发所必备的.本书首先对Win32 API函数做完整的概述:然后收录五大类函数:窗口管理.图形设备接口.系统服务.国际特性以及网络服务:在附录部分,讲解如何

搜狗2015C++工程师笔试题解题分析

试卷链接:搜狗2015 C++工程师笔试题. 1.假设整数0x12345678 存放在内存地址0x0开始的连续四个字节中 (即地址0x0到 0x3). 那么在以Little Endian字节序存储的memory中,地址0x3的地方存放的字节是: 0x12 0x34 0x56 0x78 分析:选D.小端法和大端法. a) Little-Endian就是低位字节排放在内存的低地址端, 高位字节排放在内存的高地址端. b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地