news 2026/3/18 17:39:30

汇编语言全接触-66.Win32汇编教程十

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
汇编语言全接触-66.Win32汇编教程十

在这儿下载本节的所有源程序

概述

Windows 的定时器是一种输入设备,它周期性地在指定的间隔时间通知应用程序。它可以用向指定窗口发送 WM_TIMER 消息或者调用指定的过程来执行用户的程序。定时器的应用主要包括下面一些地方:

时钟程序 - 显然,这是定时器最直接的应用。

多任务 - 如果程序有大量的数据处理,除了用多线程的办法,还可以用定时器,在每一个定时器消息中处理一小块内容。

定时显示程序的状况 - 定时器就相当于 Dos 编程中的自己挂接在 int 1ch 上面的要定时处理的程序,它可以定时显示程序运行的情况,如发送了多少内容,接收了多到内容等等。

在游戏程序中使用定时器可以消除在不同处理器下用延时来保持速度一致所造成的误差。

用于数据流处理 - 在音频、视频的播放中,需要隔一段时间处理一段数据。

总的来说,在 Dos 下实现精确定时的唯一方法是在 int 1ch 时钟中断中处理程序,但你使用起来必须遵守很多的规范,而在 Windows 的定时器中,你可以用 SetTimer 函数分配不止一个的定时器,比如说,在你的文本编辑程序中,你可以使用一个间隔1秒的定时器来在状态栏中显示时钟,同时分配一个10分钟的定时器来实现定时存盘的功能。定时器实际上是 Windows 对时钟中断的一种扩展,它的本质还是基于时钟中断的,所以你实际上无法把定时器的间隔设置到55毫秒以下,另外,定时器的精度也是以55毫秒为倍数的,比如说,你设置了一个1秒的定时器,它实际上是在每989毫秒的时候发生的。和在 Dos 下使用时钟中断,windows 的定时器还有下面一些要点:

在 Dos 中,你的程序随时可能被 int 1ch 打断,而在Windows 中,Windows 通过 WM_TIMER 消息把定时器消息放入正常的消息队列中,所以你不必担心你的程序在别的处理中被定时器打断。

不可能有同时两条以上的 WM_TIMER 消息,如果在一个还在消息队列中,窗口再得到一条 WM_TIMER 消息,两条消息会被合并为一条,所以在程序比较忙的时候可能会丢失 WM_TIMER 消息。

WM_TIMER 消息的级别是很低的,程序只有在消息队列中没有其他消息的情况下,才会接收 WM_TIMER 消息,你可以通过下马方法验证:在一个设置了定时器的窗口上按住标题栏移动窗口,你会发现定时器停止了工作,当你松开鼠标后,在这个过程中丢失的 WM_TIMER 消息并没有被补上,所以如果你设计一个时钟程序,你不能使用定时器消息来计数,而必须在消息中每次获取正确的系统时间。

讲了这么多定时器的特点,下面是定时器相关的API,你会发现除了在使用中要注意的这些特性,定时器的API真是又少又简单:

建立定时器

SetTimer(

HWND hWnd, // handle of window for timer messages

UINT nIDEvent, // timer identifier

UINT uElapse, // time-out value

TIMERPROC lpTimerFunc // address of timer procedure

);

hWnd 是 windows 发送 WM_TIMER 的窗口,nIDEvent 是定时器的编号,在 WM_TIMER 中出现在 wParam 参数中,用来区分在多个定时器的情况下,这条消息是由哪个定时器产生的。uElapse 是定时器间隔的毫秒数,如果你要设置一个1秒的定时器,这个值就是1000,lpTimerFunc 是处理定时器消息的过程,如果这个参数不是 NULL,windows 在到时间后会调用lpTimerFunc 指定的过程,调用的参数是 CALLBACK TimerProc(hwnd,WM_TIMER,iTimerID,dwTime),iTimerID 是定时器 ID,dwTime 是系统时间;如果 lpTimerFunc 参数是 NULL,Windows 会把 WM_TIMER 消息放入消息循环中,消息的 hWnd 是第一个参数中指定的 hWnd,也就是说向这个窗口发送了 WM_TIMER 消息。

另外,如果你的程序没有窗口,你也可以用这种办法建立定时器:invoke SetTimer,NULL,NULL,uElapse,TimerProc,函数会返回一个系统定义的 TimerID供你在 KillTimer 中使用。

取消定时器

KillTimer(

HWND hWnd, // handle of window that installed timer

UINT uIDEvent // timer identifier

);

取消定时器只需对应 SetTimer 时的 hWnd 和 uIDEvent 调用 KillTimer 函数就行了。

在本节的例子程序中,我在对话框中的 WM_INIT 消息中用 SetTimer 建立两个定时器,时间分别是500ms 和 200ms,然后在间隔0.5秒的定时器消息中更换按钮上的图片,在间隔 0.2 秒的定时器消息中更换标题栏上的小图标,你就可以看到动画的效果了。

源程序 - 汇编源文件

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; Programmed by 罗云彬, bigluo@telekbird.com.cn

; Website: http://asm.yeah.net

; LuoYunBin's Win32 ASM page (罗云彬的编程乐园)

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 版本信息

; 汇编教程附带例子程序 - 定时器的使用

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

.386

.model flat, stdcall

option casemap :none ; case sensitive

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; Include 数据

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

include windows.inc

include user32.inc

include kernel32.inc

include comctl32.inc

include comdlg32.inc

includelib user32.lib

includelib kernel32.lib

includelib comctl32.lib

includelib comdlg32.lib

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; Equ 数据

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

IDI_MAIN equ 1

IDI_MOON1 equ 2

IDI_MOON2 equ 3

IDI_MOON3 equ 4

IDI_MOON4 equ 5

IDI_MOON5 equ 6

IDI_MOON6 equ 7

IDI_MOON7 equ 8

IDI_MOON8 equ 9

DLG_MAIN equ 1000

ID_MOON equ 1001

ID_TIMER1 equ 1

ID_TIMER2 equ 2

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 数据段

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

.data?

hInstance dd ?

dwCounter1 dd ?

dwCounter2 dd ?

hIcon1 dd ?

hIcon2 dd ?

hIcon3 dd ?

hIcon4 dd ?

hIcon5 dd ?

hIcon6 dd ?

hIcon7 dd ?

hIcon8 dd ?

szBuffer db 256 dup (?)

.data

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 代码段

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

.code

;********************************************************************

_ProcDlgMain proc uses ebx edi esi, \

hWnd:DWORD,wMsg:DWORD,wParam:DWORD,lParam:DWORD

mov eax,wMsg

;********************************************************************

.if eax == WM_CLOSE

invoke KillTimer,hWnd,ID_TIMER1

invoke KillTimer,hWnd,ID_TIMER2

invoke EndDialog,hWnd,NULL

;********************************************************************

.elseif eax == WM_INITDIALOG

mov edi,offset hIcon1

mov ebx,IDI_MOON1

mov ecx,8

@@:

push ecx

invoke LoadIcon,hInstance,ebx

cld

stosd

inc ebx

pop ecx

loop @B

invoke SetTimer,hWnd,ID_TIMER1,500,NULL

invoke SetTimer,hWnd,ID_TIMER2,200,NULL

invoke SendMessage,hWnd,WM_SETICON,ICON_SMALL,hIcon1

;********************************************************************

.elseif eax == WM_TIMER

.if wParam == ID_TIMER1

inc dwCounter1

.if dwCounter1 == 8

mov dwCounter1,0

.endif

mov eax,dwCounter1

shl eax,2

add eax,offset hIcon1

mov eax,[eax]

invoke SendMessage,hWnd,WM_SETICON,ICON_SMALL,eax

.else

inc dwCounter2

.if dwCounter2 == 8

mov dwCounter2,0

.endif

mov eax,dwCounter2

shl eax,2

add eax,offset hIcon1

mov eax,[eax]

invoke SendDlgItemMessage,hWnd,ID_MOON,BM_SETIMAGE,IMAGE_ICON,eax

.endif

;********************************************************************

.else

mov eax,FALSE

ret

.endif

mov eax,TRUE

ret

_ProcDlgMain endp

;********************************************************************

start:

invoke GetModuleHandle,NULL

mov hInstance,eax

invoke DialogBoxParam,hInstance,DLG_MAIN,NULL,offset _ProcDlgMain,0

invoke ExitProcess,NULL

;********************************************************************

end start

程序的分析和要点

有了上面的介绍,这个程序是很容易看懂的,在 WM_TIMER 消息中,通过 wParam 中的 TimerID 可以区分是哪个定时器产生的消息。在 WM_CLOSE 消息中,通过 KillTimer 来取消定时器。本程序中的的图标定义在资源文件中,在对话框建立的时候,先用 LoadIcon 装入,然后为两个定时器分别保存一个图片编号 dwCounter1 和 dwCounter2,在定时器消息中分别用 WM_SETICON 和 BM_SETIMAGE 消息来对窗口标题的图标和按钮的图标进行设置。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/17 10:41:04

Keil使用教程:超详细版IDE界面功能与工具栏说明

Keil使用教程:从界面小白到调试高手的实战进阶指南你有没有过这样的经历?打开Keil,面对密密麻麻的菜单和按钮,却不知道从哪下手;编译报错一堆警告,却找不到根源;调试时想看个寄存器值&#xff0…

作者头像 李华
网站建设 2026/3/18 9:20:46

工业物联网中Java校准系统的4大关键挑战及应对策略

第一章:Java 工业传感器校准的背景与意义 在现代工业自动化系统中,传感器作为数据采集的核心组件,其测量精度直接影响生产过程的稳定性与产品质量。由于环境温度、机械磨损和电子漂移等因素的影响,传感器输出值往往会产生偏差&…

作者头像 李华
网站建设 2026/3/18 11:44:09

NXP Kinetis系列在Keil芯片包中的Cortex-M支持分析

NXP Kinetis 与 Keil 的深度协同:从芯片包到 Cortex-M 开发的实战解析你有没有过这样的经历?刚接手一个 Kinetis 项目,打开 Keil Vision 准备开工,结果卡在第一步——选完芯片型号后,工程里空空如也,头文件…

作者头像 李华
网站建设 2026/3/14 6:17:03

掌握Java Vector API的3个黄金法则(x64 SIMD优化内幕曝光)

第一章:Java Vector API与x64架构的协同演进Java Vector API 是 Project Panama 的核心组成部分,旨在通过显式支持 SIMD(单指令多数据)操作,充分发挥现代 CPU 架构的并行计算能力。随着 x64 架构在服务器和桌面端的广泛…

作者头像 李华
网站建设 2026/3/14 12:04:19

Java如何实现毫秒级工业传感器校准(仅限高级工程师掌握的技术)

第一章:Java在工业传感器校准中的核心作用在现代工业自动化系统中,传感器作为数据采集的前端设备,其精度直接影响整个系统的可靠性。Java凭借其跨平台性、稳定性和丰富的库支持,在传感器校准流程中扮演着关键角色。无论是嵌入式设…

作者头像 李华
网站建设 2026/3/16 20:18:02

手把手教你完成Proteus下载安装与启动

从零开始搭建电子设计仿真环境:Proteus安装实战与深度避坑指南 你有没有过这样的经历? 刚下定决心要学单片机,翻开教材第一步就是“打开Proteus画个LED闪烁电路”,结果点开安装包还没进软件,就已经被一堆报错拦在门外…

作者头像 李华