Win32 计时器

计时器精确吗?

计时器并不精确。有两个原因:

  原因一:Windows计时器是硬件和ROM BIOS架构下之计时器一种相对简单的扩充。回到Windows以前的MS-DOS程序写作环境下,应用程式能够通过拦截者称为timer tick的BIOS 中断来实现时钟或计时器。一些为MS-DOS编写的程序自己拦截这个硬件中断以实现时钟和计时器。这些中断每54.915毫秒产生一次,或者大约每秒18.2次。这是原始的IBM PC的微 处理器频率值4.772720 MHz被218所除而得出的结果。在Windows 98中,计时器与其下的PC计时器一样具有55毫秒的解析度。在Microsoft Windows NT中,计时器的解析度为10毫秒 。Windows应用程式不能以高于这些解析度的频率(在Windows 98下,每秒18.2次,Windows NT下,每秒大约100次)接收WM_TIMER消息。在SetTimer中指定的时间间隔总是截尾 后tick数的整数倍。例如,1000毫秒的间隔除以54.925毫秒,得到18.207个tick,截尾后是18个tick,它实际上是989毫秒。对每个小于55毫秒的间隔,每个tick都会产生一个 WM_TIMER消息。
  可见,计时器并不能严格按照指定的时间间隔发送WM_TIMER消息,它总要相差那么几毫秒。

  即使忽略这几个毫秒的差别,计时器仍然不精确。

  原因二
  WM_TIMER消息放在正常的消息队列之中,和其他消息排列在一起,因此,如果在SetTimer中指定间隔为1000毫秒,那么不能保证程序每1000毫秒或者989毫秒就会收到一个 WM_TIMER消息。如果其他程序的执行事件超过一秒,在此期间内,您的程式将收不到任何WM_TIMER讯息。事实上, Windows对WM_TIMER消息的处理非常类似于对WM_PAINT消息的处理,这两个消息都是低优先级的,程序只有在消息队列中没有其他消息时才接收它们。WM_TIMER还在另一方面和WM_PAINT相似:Windows不能持续向消息队列中放入多个WM_TIMER讯息,而是将多余的WM_TIMER消息组合成一个消息。因此,应用程序不会一次收到多个这样的消息,尽管可能在短时间内得到两个WM_TIMER消息。应用程序不能确定这种处理方式所导致的WM_TIMER消息「遗漏」的数目。
  可见,WM_TIMER消息并不能及时被应用程序所处理,WM_TIMER在消息队列中的延误可能就不能用毫秒来计算了。

由以上两点,你不能通过在处理WM_TIMER时一秒一秒计数的方法来计时。如果要实现一个时钟程序,可以使用系统的时间函数如GetLocalTime ,而在时钟程序中,计时器的作用是定时调用GetLocalTime获得新的时间并刷新时钟画面,当然这个刷新的间隔要等于或小于1秒。

什么时候我们需要用到SetTimer函数呢?当你需要每个一段时间执行一件事的的时候就需要使用SetTimer函数了。

  让我们先来看看SetTimer函数的原型: UINT SetTimer(UINT nIDEvent,UINT nElapse,void(CALLBACK EXPORT *lpfnTimer)(HWND,UINT ,YINT ,DWORD))

  当使用SetTimer函数的时候,就会生成一个计时器。

  函数中nIDEvent指的是计时器的标识,也就是名字。

  nElapse指的是时间间隔,也就是每隔多长时间触发一次事件。

  第三个参数是一个回调函数,在这个函数里,放入你想要做的事情的代码,你可以将它设定为NULL ,也就是使用系统默认的回调函数,系统默认认的是onTime函数。这个函数怎么生成的呢?你需要在需要计时器的类的生成onTime函数:在ClassWizard里,选择需要计时器的类,添加WM_TIME消息映射,就自动生成onTime函数了。然后在函数里添加代码,让代码实现功能。每隔一段时间就会自动执行一次。 例: SetTimer(1,1000,NULL); 1:计时器的名称 ; 1000:时间间隔,单位是毫秒; NULL:使用onTime函数。

  当不需要计时器的时候调用KillTimer(nIDEvent); 例如:KillTimer(1); 2. 或许你会问,如果我要加入两个或者两 个以上的 timer怎么办? 继续用SetTimer函数吧,上次的timer的ID是1,这次可以是2,3,4。。。。 SetTimer(2,1000,NULL); SetTimer(3,500,NULL); 嗯,WINDOWS会协调他们 的。当然onTimer函数体也要发生变化,要在函数体内添加每一个timer的处理代码: onTimer(nIDEvent) { switch(nIDEvent) { case 1:........; break; case 2:.......; break; case 3:......; break; } }

***********************************************************

SDK编程笔记 — 计时器篇两个计时器API的讨论

  SetTimer函数用于创建一个计时器,KillTimer函数用于销毁一个计时器。计时器属于系统资源,使用完应及时销毁。

  SetTimer的函数原型如下:
  UINT_PTR SetTimer( HWND hWnd, UINT_PTR nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc ) ;
  其中
  hWnd是和timer关联的窗口句柄,此窗口必须为调用SetTimer的线程所有;如果hWnd为NULL,没有窗口和timer相关联并且nIDEvent参数被忽略
  nIDEvent是timer的标识,为非零值;如果hWnd为NULL则被忽略;如果hWnd非NULL而且与timer相关联的窗口已经存在一个为此标识的timer,则此次SetTimer调用将用新的 timer代替原来的timer。timer标识和窗口相关,两个不同的窗口可以拥有nIDEvent相同的tiemruElapse是以毫秒指定的计时间隔值,范围为1毫秒到4,294,967,295毫秒(将近50天),这个值指示Windows每隔多久时间给程序发送WM_TIMER消息。

  lpTimerFunc是一个回调函数的指针,俗称TimerFunc;如果lpTimerFunc为NULL,系统将向应用程序队列发送WM_TIMER消息;如果lpTimerFunc指定了一个值,DefWindowProc将 在处理WM_TIMER消息时调用这个lpTimerFunc所指向的回调函数,因此即使使用TimerProc代替处理WM_TIMER也需要向窗口分发消息。

  关于SetTimer的返回值:如果hWnd为NULL,返回值为新建立的timer的ID,如果hWnd非NULL,返回一个非0整数,如果SetTimer调用失败则返回0

  KillTimer的函数原型为:BOOL KillTimer( HWND hWnd, UINT_PTR uIDEvent ) ; 参数意义同SetTimer。
  关于KillTimer对消息队列中剩余未处理的WM_TIMER消息的影响,MSDN和Programming Windows上的说法完全相反。MSDN的说法很干脆:The KillTimer function does not

remove WM_TIMER messages already posted to the message queue. 而petzold则说 The KillTimer call purges the message queue of any pending WM_TIMER messages.

Your program will never receive a stray WM_TIMER message following a KillTimer call. (KillTimer消除消息队列中任何未处理的WM_TIMER消息,调用KillTimer后你的程

序永远不会收到一条“漂泊游荡”的WM_TIMER消息)

关于WM_TIMER消息

  wParam为计时器的ID;如果需要设定多个计时器,那么对每个计时器都使用不同的计时器ID。wParam的值将随传递到窗口过程中的WM_TIMER消息的不同而不同。
  lParam为指向TimerProc的指针,如果调用SetTimer时没有指定TimerProc(参数值为NULL),则lParam为0(即NULL)。
  可以通过在窗口过程中提供一个WM_TIMER case处理这个消息,或者,默认窗口过程会调用SetTimer中指定的TimerProc来处理WM_TIMER消息

使用计时器的三种方法

  如果在程序的整个执行过程中使用计时器,一般在处理WM_CREATE消息时或WinMain中消息循环前调用SetTimer,在处理WM_DESTROY消息时或在WinMain中消息循环后return前调用KillTimer。根据SetTimer中的参数不同,有三种方法使用计时器。

  方法一:调用SetTimer时指定窗口句柄hWnd,nIDEvent中指定计时器ID,将lpTimerFunc置NULL从而不使用TimerProc;在窗口过程中处理WM_TIMER消息。调用KillTimer时,使

用SetTimer中指定的hWnd和id。最好使用#define定义timer的id,例如:

#define ID_TIMER 1
SetTimer(hWnd,ID_TIMER,1000,NULL) ;
KillTimer(hWnd,ID_TIMER) ;

  方法二调用SetTimer时指定窗口句柄hWnd,nIDEvent中指定计时器ID,lpTimerFunc参数不为NULL而指定为TimerProc函数的指针。这种方法使用TimerProc函数(名字可自定)

处理WM_TIMER消息:

VOID CALLBACK TimerProc ( HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
 //处理WM_TIMER讯息
}

   TimerProc的参数hwnd是在调用SetTimer时指定的窗口句柄。Windows只把WM_TIMER消息送给TimerProc,因此消息参数总是等于WM_TIMER。iTimerID值是计时器ID,dwTimer值是与从GetTickCount函数的返回值相容的值。这是自Windows启动后所经过的毫秒数。 使用这种方法时,相关函数调用的形式为:

SetTimer(hWnd,ID_TIMER,1000,TimerProc) ;
KillTimer(hWnd,ID_TIMER) ;

  方法三:调用SetTimer时不指定窗口句柄(为NULL),iTimerID参数自然被忽略,lpTimerFunc不为NULL而指定为TimerProc的指针。正如上面SetTimer的讨论中所说的,此时

SetTimer的返回值正是新建立的计时器的ID,需将这个ID保存以供KillTimer销毁计时器时所用。当然,KillTimer的hWnd参数也置为NULL。这种方法同样用TimerProc处理WM_TIMER 消息。

UINT_PTR iTimerID ;
iTimerID = SetTimer(NULL,0,1000,TimerProc) ;
KillTimer(NULL,iTimerID) ;

  使用这种方法的好处是不必自己指定计时器ID,这样就不必担心用错ID。

使用多个计时器

  使用多个计时器只要在建立计时器时指定不同的ID。比如用上面所述方法一时的情况:

#define TIMER_SEC 1
#define TIMER_MIN 2
然后使用两个SetTimer来设定两个计时器:
SetTimer (hwnd, TIMER_SEC, 1000, NULL) ;
SetTimer (hwnd, TIMER_MIN, 60000, NULL) ;
WM_TIMER的处理如下所示:
case WM_TIMER:
 switch (wParam)
 {
  case TIMER_SEC:
   //每秒一次的处理
   break ;
  case TIMER_MIN:
   //每分钟一次的处理
   break ;
 }
 return 0 ;

改变计时器的时间间隔

  如果想将一个已经存在的计时器设定为不同的时间间隔,可以简单地用不同的时间值再次调用SetTimer。

Win32 计时器

时间: 2024-08-03 06:08:43

Win32 计时器的相关文章

[转载]win32 计时器使用

在工业生产控制系统中,有许多需要定时完成的操作,如定时显示当前时间,定时刷新屏幕上的进度条,上位机定时向下位机发送命令和传送数据等.特别是在对控制性能要求较高的实时控制系统和数据采集系统中,就更需要精确定时操作. 众所周知,Windows 是基于消息机制的系统,任何事件的执行都是通过发送和接收消息来完成的. 这样就带来了一些问题,如一旦计算机的CPU被某个进程占用,或系统资源紧张时,发送到消息队列 中的消息就暂时被挂起,得不到实时处理.因此,不能简单地通过Windows消息引发一个对定时要求 严

创建一个简单的计时器

#include<windows.h>#include<tchar.h>#define TIME 5 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){    WNDCLASS clock;    static TCHAR* szAppN

跟我一起玩Win32开发(转自CSDN-东邪独孤)

跟我一起玩Win32开发(1):关于C++的几个要点 跟我一起玩Win32开发(2):完整的开发流程 跟我一起玩Win32开发(3):窗口的重绘 跟我一起玩Win32开发(4):创建菜单 跟我一起玩Win32开发(5):具有单选标记的菜单 跟我一起玩Win32开发(6):创建右键菜单 跟我一起玩Win32开发(7):多边形窗口 跟我一起玩Win32开发(8):绘图(A) 跟我一起玩Win32开发(9):绘图(B) 跟我一起玩Win32开发(10):绘图(C) 跟我一起玩Win32开发(11):使

Direct3D 10学习笔记(二)——计时器

本篇将简单整理Direct3D 10的计时器实现,具体内容参照< Introduction to 3D Game Programming with DirectX 10>(中文版有汤毅翻译的电子书<DirectX 10 3D游戏编程入门>). 1.高精度性能计数器 Direct3D10使用高精度性能计数器(精度达微秒级)实现精确时间测量,为了调用下面介绍的两个Win32计数器API,需要添加包含语句“#include <windows.h>” 1 BOOL QueryP

[C#]高性能计时器的实现

有时需要对程序的运行时间进行精确的计时,Windows API方法GetTickCount()返回系统启动后的毫秒数,不是很精确. Win32 API 使用 QueryPerformanceCounter() 和 QueryPerformanceFrequency() 方法支持高精度计时.这些方法,比"标准的"毫秒精度的计时方法如 GetTickCount() 之类有高得多的精度.另一方面来说,在C# 中使用"非托管"的 API 函数会有一定的开销,但比起使用一点都

C# 计时器

C# 计时器 C#中一共有4中常用的计时器: System.Timers.Timer 和 System.Windows.Forms.Timer ,它的最低识为 1/18s. timeGetTime ,它的最低识别能达到 5ms . System.Environment.TickCount ,它的最低识别为毫米级. QueryPerformanceCounter,它的最低识别为 1ms. //以QueryPerformanceCounter为例: public class Program { //

【WIN32进阶之路】:线程同步技术纲要

前面博客讲了互斥量(MUTEX)和关键段(CRITICAL SECTION)的使用,想来总觉不妥,就如盲人摸象一般,窥其一脚而言象,难免以偏概全,追加一篇博客查遗补漏. win32下的线程同步技术分为用户模式下的线程同步和用内核对象进行线程同步两大类. 用户模式下的线程同步和用内核对象进行线程同步有以下的明显差异: 1.用户模式下的线程同步不需要进入操作系统核心,直接在用户模式就可以进行操作. 2.用内核对象进行线程同步需要进入操作系统核心,用户模式切换至核心模式大约花费1000个CPU周期.

Win32界面程序

目录 一.Windows编程基础知识... 1 1.句柄... 1 2.消息... 1 (1)系统定义的消息分类... 2 (2)不同消息的取值范围... 2 (3)Windows应用中的常用消息... 2 二.Windows应用的基本结构... 3 1.数据类型... 3 2.数据结构... 4 (1)MSG.. 4 (2)WNDCLASSEX.. 4 (3)POINT. 5 (4)RECT. 5 3.入口函数WinMain() 6 (1)参数说明... 6 (2)定义窗口类... 6 (3)

带dos调试窗口的win32程序

#pragma comment( linker, "/subsystem:\"console\" /entry:\"WinMainCRTStartup\"") 方法只要在cpp文件头部加入一行: #pragma comment( linker, "/subsystem:\"console\" /entry:\"WinMainCRTStartup\"")//以console程序样式,并且制