WIN32汇编语言教程:第06章 定时器 · 6.1 定时器简介/6.2 定时器的使用(1)
在应用程序需要使用定时器时,可以用SetTimer函数向Windows申请一个定时器,要求系统在指定的时间以后“通知”应用程序,如果申请成功的话,系统会以指定的时间周期调用SetTimer函数指定的回调函数,或者向指定的窗口过程发送WM_TIMER消息,和DOS操作系统固定以55 ms的间隔触发中断服务程序相比,SetTimer函数可以指定的时间间隔更为灵活——以ms为单位,可以指定的时间周期为一个32位的整数,也就是从1~4 294 967 295 ms,这可是一个将近50天的范围!
但是在具体的使用中不要被这个参数所迷惑:由于Windows的定时器同样是基于时钟中断的,所以虽然参数的单位是ms,但精度还是55 ms,如果指定一个小于55 ms的周期,不管是1 ms还是54 ms,Windows最快也只能在每个时钟中断的时候触发这个定时器,也就是说,实际上这个定时器是以55 ms为触发周期的;另外,当指定一个时间间隔的时候,Windows以和这个间隔最接近的55 ms的整数倍时间来触发定时器,假定建立一个周期为1 000 ms的定时器,定时器的触发周期实际上不是1 s而是989 ms(55 ms×18)。
使用定时器时还有一个要点就是定时器消息是一个低级别的消息,这表现在两个方面:首先就是Windows只有在消息队列中没有其他消息的情况下才会发送WM_TIMER消息,如果窗口过程忙于处理某个消息没有返回,使消息队列中有消息积累起来,那么WM_TIMER消息就会被丢弃,在消息队列再度空闲的时候,被丢弃的WM_TIMER消息不会被补发(用一句经典的话来描述就是:“过去的就让它过去吧!”);其次,消息队列中不会有多条WM_TIMER消息,如果消息队列中已经有一条WM_TIMER消息,还没来得及处理,又到了定时的时刻,那么两条WM_TIMER消息会被合并成一条。
所以,应用程序不能依靠定时器来保证某件事情必须在规定的时刻被处理,另外,也不能依赖对定时器消息计数来确定已经过去了多少时间。
读者可以在所附光盘的Chapter06\Timer目录中找到一个例子,运行Timer.exe以后出现的界面如图6.1所示。
这个例子程序中共定义了3个定时器,第1个以250 ms为周期更换对话框上的图标;第2个以1s为单位进行计数并把结果显示在对话框上;第3个以2s为单位驱动扬声器发出“嘟嘟”的响声。为了验证WM_TIMER消息的级别,读者可以在运行中按住标题栏的“关闭”按钮不放,就可以发现3个定时器全部停止了,然后将鼠标移出“关闭”按钮并释放,定时器会重新工作,但对话框上的计数结果在定时器停止的期间并没有补上去,也就是说,在这期间,WM_TIMER消息被全部丢弃了。
6.2 定时器的使用
下面以Timer程序为例说明定时器的使用方法,这个程序的资源脚本文件定义如下:
#include <resource.h> #define DLG_MAIN 1 #define ICO_1 1 #define ICO_2 2 #define IDC_SETICON 100 #define IDC_COUNT 101 //################################################################## ICO_1 ICON "1.ico" ICO_2 ICON "2.ico" //################################################################## DLG_MAIN DIALOG 50, 50, 113, 40 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "定时器例子" FONT 9, "宋体" { ICON ICO_1, IDC_SETICON, 8, 9, 18, 21 LTEXT "计数:", -1, 35, 16, 25, 10 LTEXT "", IDC_COUNT, 62, 16, 40, 10 }
对资源的定义读者现在一定不会陌生了,这个文件中定义了两个图标和一个对话框,对话框中定义了一个图标框和两个文本框,其中的一个文本框中的文字为空,这是以后显示每秒一次的计数值用的。
Timer.asm源程序如下:
.386 .model flat,stdcall option casemap:none ;#################################################################### ; Include 文件定义 ;#################################################################### include windows.inc include user32.inc includelib user32.lib include kernel32.inc includelib kernel32.lib ;#################################################################### ID_TIMER1 equ 1 ID_TIMER2 equ 2 ICO_1 equ 1 ICO_2 equ 2 DLG_MAIN equ 1 IDC_SETICON equ 100 IDC_COUNT equ 101 ;#################################################################### ; 数据段 ;#################################################################### .data? hInstance dd ? hWinMain dd ? dwCount dd ? idTimer dd ? ;#################################################################### ; 代码段 ;#################################################################### .code ;#################################################################### ; 定时器过程 ;#################################################################### _ProcTimer proc _hWnd,uMsg,_idEvent,_dwTime pushad invoke GetDlgItemInt,hWinMain,IDC_COUNT,NULL,FALSE inc eax invoke SetDlgItemInt,hWinMain,IDC_COUNT,eax,FALSE popad ret _ProcTimer endp ;#################################################################### ; 窗口过程 ;#################################################################### _ProcDlgMain proc uses ebx edi esi,hWnd,uMsg,wParam,lParam mov eax,uMsg ;********************************************************************