当前位置: 首页 > article >正文

Windows程序中计时器WM_TIMER消息的使用

本文章是对《Windows程序设计》这本书第八章计时器的总结,如果有时间,可以去看书里的讲解,如果时间不充裕,想马上知道计时器该如何使用,欢迎阅读本文,本文已经将计时器的干货整理完毕!


什么是计时器?

首先我们先要知道什么是计时器。我们的程序首先设定一个计时器,假如我定时一分钟,那么系统就会每过一分钟就往我们程序的消息队列中插入一个WM_TIMER消息,这样,在理想的情况下,我们的窗口过程函数就会每过一分钟,处理一次WM_TIMER消息。让系统每过一段时间(这个时间由我们自己设定),向我们程序发送一个WM_TIMER消息(其实是放到消息队列),这就是计时器。


计时器有什么用?

如果我们现在有一个需求, 每过一分钟刷新一下程序的界面,那么,我们程序如何做才能知道一分钟过去了呢?我们可以循环获取时间戳,然后来比较差值,但有了计时器,我们就有了另一种方法,我们程序首先设定一个计时器,这个计时器的时间间隔是一分钟,然后程序在每过一分钟都会收到WM_TIMER消息,我们就可以在处理这个消息的时候写代码来刷新程序界面。


计时器有哪些注意要点?

1.计时器并不是十分精准的

当我们设置一个时间(以毫秒为单位),Windows系统会维护一个计数值,硬件时钟滴答出现一次,那么Windows维护的计数值就会减1,当计数值减到0,Windows会把WM_TIMER消息放到相应程序的消息队列,然后把计数值恢复到原始值。既然如此,Windows投递WM_TIMER的时间会与硬件时钟滴答有关,Windows 98上,计时器有55毫秒的周期,Windows NT上,计时器有10毫秒的周期。

接下来拿Windows 98这个计时器周期举例,55毫秒为一个滴答,当我们设置1000毫秒(1秒)的时候,系统会把1000除以55等于18.2,然后保留整数18这个数值作为计数值,也就是18个硬件时钟滴答(990毫秒)就会让系统将一个WM_TIMER消息放到程序的消息队列中。如果设置计时器的时候时间小于55毫秒,则系统的计数值是一个滴答,每过55毫秒,系统就会将WM_TIMER放到程序的消息队列。

2.计时器消息是同步的

当设定的时间一到,系统会将WM_TIMER消息放到程序的消息队列,当程序之前的消息都处理完成,才会通过消息循环取出计时器消息,而不用担心计时器消息一产生,就会打断程序正在处理的其他消息。WM_TIMER消息是低优先级的,只有当消息队列其他消息处理完毕,才会去处理WM_TIMER。

3.系统不会连续产生多个WM_TIMER消息

WM_TIMER消息和WM_PAINT消息一样,当程序处理某个消息很费时,消息队列中有WM_TIMER消息处于等待被处理,系统是不会将WM_TIMER放到队列里,而是将WM_TIMER消息合并到一起,形成一个消息,这样程序就不会收到大量的WM_TIMER消息。

总结:

计时器消息虽然不十分精准,但已经很精准了,只差几毫秒,对于大部分程序,我们可以忽略这种误差。另外如果我们处理一个耗时的消息时,计时器消息有可能丢失,或者等待太久,这种情况我们可以利用多线程,将耗时的工作放到线程中,从而避免阻塞。


计时器的三种使用方法是什么?

我们程序里可以使用SetTimer函数来设定一个计时器。当你设定计时器后,程序就会在一定的时间收到WM_TIMER消息,如果想停止这个计时器,可以用KillTimer函数。

用法1.
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{case WM_CREATE:SetTimer(hwnd, ID_TIMER, 1000, NULL); // ID_TIMER是计时器ID,是自定义宏return 0;case WM_TIMER:// 你想在计时器里处理的内容return 0;case WM_DESTROY:KillTimer(hwnd, ID_TIMER);PostQuitMessage(0);return 0;// 其他消息处理return DefWindowProc(hwnd, message, wParam, lParam);
}

这里我直接列出了应用程序的窗口过程函数。我们在处理WM_CREATE消息时设置了一个计时器,第一个参数是窗口的句柄,第二个参数是计时器ID,是一个非负整数,如果你想设置多个计时器,那么设置每一个计时器的时候,ID是不能重复的,也不能是0,当我们需要KillTimer的时候,将要停止的计时器的ID传入即可,第三个参数是想要定时的时间,以毫秒为单位,这里我们设置1000毫秒,也就是1秒,第四个参数是NULL。我们设置好计时器后,程序就会在每一秒中,收到WM_TIMER消息,然后我们在收到消息的时候进行处理即可。在程序结束前,我们调用KillTimer函数来关闭计时器,开启计时器后不忘关闭,是一个好习惯。

用法2.
VOID CALLBACK TimerProc(HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{// 你想处理的代码
}LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{case WM_CREATE:SetTimer(hwnd, ID_TIMER, 1000, TimerProc); // ID_TIMER是计时器ID,是自定义宏return 0;case WM_DESTROY:KillTimer(hwnd, ID_TIMER);PostQuitMessage(0);return 0;// 其他消息处理return DefWindowProc(hwnd, message, wParam, lParam);
}

这个用法和用法1类似,唯一不同的是处理计时器代码的位置,用法1我们在SetTimer函数的第四个参数设置为NULL,这个参数代表一个回调函数指针,当设置为NULL,则处理代码要在窗口过程函数的WM_TIMER中进行处理,一旦这个参数设置了回调函数,那么在收到WM_TIMER消息之后,会直接扔进这个回调函数进行处理,而不用进入到窗口过程函数中处理WM_TIMER。这个回调函数的写法大家要注意,函数名可以随便起,但是函数的参数和返回值类型,要和上面代码中的例子保持一致,另外别忘了在回调函数名前面加CALLBACK。

用法3.
// 设置计时器
iTimerID = SetTimer(NULL, 0, 1000, TimerProc);// 结束计时器
KillTimer(NULL, iTimerID);

这个方法用起来比较另类,它的用法和用法2类似,要有自己的计时器回调函数。首先第一个参数是NULL,第二个参数本来是设置计时器ID,但是这里设置0,第三个和第四个参数和用法2一样,调用完这个函数之后会返回一个计时器ID,我们要在某个位置保存这个计时器ID,方便关掉这个计时器的时候用。KillTimer的第一个参数也是NULL。


结语

至此,Windows计时器的用法已经讲解完毕,我知道,这个计时器的用法很简单,每当我读《Windows程序设计》读不进去的时候,都会回到计时器这一章来寻找安慰。

相关文章:

Windows程序中计时器WM_TIMER消息的使用

本文章是对《Windows程序设计》这本书第八章计时器的总结,如果有时间,可以去看书里的讲解,如果时间不充裕,想马上知道计时器该如何使用,欢迎阅读本文,本文已经将计时器的干货整理完毕! 什么是计…...

Apache httpclient okhttp(1)

学习链接 Apache httpclient & okhttp(1) Apache httpclient & okhttp(2) httpcomponents-client github apache httpclient文档 apache httpclient文档详细使用 log4j日志官方文档 【Java基础】- HttpURLConnection…...

微信小程序—路由

关于 app.json 中的配置 app.json 主要是对整个小程序进行一个全局的配置。 pages:在这个配置项目中,就可以配置小程序里面的页面,小程序默认显示 pages 数组中的第一个页面windows:主要配置和导航栏相关的 当然,在…...

人工智能驱动的数据仓库优化:现状、挑战与未来趋势

1. 引言:数据仓库的演进与人工智能驱动优化的兴起 现代数据仓库的复杂性和规模正以前所未有的速度增长,这主要是由于数据量、种类和产生速度的急剧增加所致。传统的数据仓库技术在应对这些现代数据需求方面显得力不从心,这催生了对更先进解决…...

LVS高可用负载均衡

一、项目图 二、主机规划 主机系统安装应用网络IPclientredhat 9.5无NAT192.168.72.115/24lvs-masterredhat 9.5ipvsadm,keepalivedNAT192.168.72.116/24 VIP 192.168.72.100/32lvs-backupredhat 9.5ipvsadm,keepalivedNAT192.168.72.117/24 VIP 192.168…...

脑影像分析软件推荐 | JuSpace

目录 1. 软件界面 2.工具包功能简介 3.软件安装注意事项 参考文献: Dukart J, Holiga S, Rullmann M, Lanzenberger R, Hawkins PCT, Mehta MA, Hesse S, Barthel H, Sabri O, Jech R, Eickhoff SB. JuSpace: A tool for spatial correlation analyses of magne…...

集合框架——常用类

集合框架的理解 就是一个常用类 集合主要有三种类型【集合与集合之间可以互转】 List(列表,是接口)Set(集合,是接口)Map(键值对,是接口)Collection是以上及所有集合的根接口,其里面的子类,一部分支持集合有序元素可重复,一部分支持集合无序元素不重复 可重复和不…...

【NLP 面经 7、常见transformer面试题】

目录 1. 为何使用多头注意力机制? 2. Q和K使用不同权重矩阵的原因 3. 选择点乘而非加法的原因 4. Attention进行scaled的原因 5. 对padding做mask操作 6. 多头注意力降维原因 7. Transformer Encoder模块简介 8. 乘以embedding size的开方的意义 9. 位置编码 10. 其…...

分布式事务解决方案全解析:从经典模式到现代实践

前言 在分布式系统中,数据一致性是一个核心问题。随着微服务架构的普及,跨服务、跨数据库的操作变得越来越普遍,如何保证这些操作的原子性、一致性、隔离性和持久性(ACID)成为了一个极具挑战性的任务。本文将全面介绍…...

软件工程面试题(二十七)

1、j a v a 对象初始化顺序 1.类的初始化(initialization class & interface) 2.对象的创建(creation of new class instances) 顺序:应为类的加载肯定是第一步的,所以类的初始化在前。大体的初始化顺序是: 类初始化 -> 子类构造函数 -> 父类构造函数 -&g…...

fastGPT—nextjs—mongoose—团队管理之部门相关api接口实现

创建部门或者子部门 import type { NextApiRequest, NextApiResponse } from next; import { NextAPI } from /service/middleware/entry; import { MongoOrgModel } from fastgpt/service/support/permission/org/orgSchema;async function handler(req: NextApiRequest, res…...

C++ 数据竞态检查

-fsanitizethread 编译时,添加参数-fsanitizethread -g,可以运行态检查数据竞态问题,包括: 数据竞态死锁锁、条件变量错误使用 check_tsan 开源库 yalantinglibs有段检查编译器是否支持 fsanitize 编译参数的宏,挺…...

逛好公园的好处

逛公园和软件开发看似是两个不同的活动,但它们之间存在一些有趣的关联和相互促进的关系: 激发创造力:公园中的自然景观、多样的人群以及各种活动能为开发者带来新的灵感和创意。软件开发过程中,从公园中获得的创意可以帮助开发者设…...

C++开发工具全景指南

专业编译与调试工具深度解析 2025年4月 编译器套件 GNU Compiler Collection (GCC) GNU编译器套件是自由软件基金会开发的跨平台编译器系统,支持C、C、Objective-C、Fortran、Ada等多种编程语言。作为Linux系统的标准编译器,GCC以其强大的优化能力和…...

【网络安全】 防火墙技术

防火墙是网络安全防御的重要组成部分,它的主要任务是阻止或限制不安全的网络通信。在这篇文章中,我们将详细介绍防火墙的工作原理,类型以及如何配置和使用防火墙。我们将尽可能使用简单的语言和实例,以便于初学者理解。 一、什么…...

文档的预解析

1. 预解析的核心目标 浏览器在正式解析(Parsing)HTML 前,会启动一个轻量级的 预解析器(Pre-Parser),快速扫描文档内容,实现: 提前发现并加载关键资源(如 CSS、JavaScrip…...

理解“功能内聚”

链接: 理解“偶然内聚” 理解“逻辑内聚” 理解“时间内聚” 理解“过程内聚” 理解“通信内聚” 理解“顺序内聚” 理解“功能内聚” 功能内聚(Functional Cohesion)是最高级别的内聚形式,指的是模块内的所有元素都紧密地围绕着一…...

windows 常用命令总结

工作中用到的 Linux 总结(持续更新中...)_linux工作经验-CSDN博客 PS: 推荐使用 powershell 而不是 cmd,因为PowerShell 是一个更先进和功能更强大的工具( powershell 有命令记忆功能,比较方便&#xff09…...

记一次表格数据排序优化(一)--排序30000条数据有多卡

目录 需求 第一次尝试 运行环境 思路 存储 排序 触发排序操作 如何实现高效的排序 关键1 关键2 关键3 磨刀不误砍柴工 关键4 代码 效果 卡顿原因分析 原因1 原因2 第二次尝试 需求 1 我的qt程序通过表格显示30000条数据。数据来自udp,udp每隔10秒…...

图形渲染中的定点数和浮点数

三种API的NDC区别 NDC全称,Normalized Device Coordinates Metal、Vulkan、OpenGL的区别如下: featureOpenGL NDCMetal NDCVulkan NDC坐标系右手左手右手z值范围[-1,1][0,1][0,1]xy视口范围[-1,1][-1,1][-1,1] GPU渲染的定点数和浮点数 定点数类型&a…...

【深度学习】CNN简述

文章目录 一、卷积神经网络(CNN)二、CNN结构特性1. CNN 典型结构2. 局部连接3. 权重共享4.空间或时间上的次采样 三、理解层面 一、卷积神经网络(CNN) 卷积神经网络(Convolutional Neural Network,CNN)是一种用于处理…...

强化学习课程:stanford_cs234 学习笔记(3)introduction to RL

文章目录 前言7 markov 实践7.1 markov 过程再叙7.2 markov 奖励过程 MRP(markov reward process)7.3 markov 价值函数与贝尔曼方程7.4 markov 决策过程MDP(markov decision process)的 状态价值函数7.4.1 状态价值函数7.4.2 状态…...

紫檀博物馆一游与软件开发

今天去逛了中国紫檀博物馆,里边很多层展品,也有一些清代的古物,檀木,黄花梨木家具和各种摆件,馆主陈丽华女士也是发心复原、保留和弘扬中国的传统文化,和西游记唐僧扮演者迟成瑞先生一家。 每一件展品都精…...

RocketMQ初认识

ProducerCustomerNameServer: Broker的注册服务发现中心BrokerServer:主要负责消息的存储、投递和查询以及服务高可用保证 RocketMQ的集群部署: 单个master的分支多个Master 模式:集群中有多个 Master 节点,彼此之间相互独立。生产者可以将消…...

第十三章:持久化存储_《凤凰架构:构建可靠的大型分布式系统》

第十三章 持久化存储 一、Kubernetes存储设计核心概念 (1)存储抽象模型 PersistentVolume (PV):集群级别的存储资源抽象(如NFS卷/云存储盘)PersistentVolumeClaim (PVC):用户对存储资源的声明请求&#…...

Chrome开发者工具实战:调试三剑客

在前端开发的世界里,Chrome开发者工具就是我们的瑞士军刀,它集成了各种强大的功能,帮助我们快速定位和解决代码中的问题。今天,就让我们一起来看看如何使用Chrome开发者工具中的“调试三剑客”:断点调试、调用栈跟踪和…...

教程:如何使用 JSON 合并脚本

目录 1. 介绍 2. 使用方法 3. 注意事项 4. 示例 5.完整代码 1. 介绍 该脚本用于将多个 COCO 格式的 JSON 标注文件合并为一个 JSON 文件。COCO 格式常用于目标检测和图像分割任务,包含以下三个主要部分: "images":图像信息&a…...

C++/Qt 模拟sensornetwork的工作

C/Qt 可视化模拟sensornetwork的工作 C/Qt 模拟sensornetwork的工作 C/Qt 可视化模拟sensornetwork的工作内容简介(一) 需求和规格说明(1)问题描述(2)设计目的(3)基本要求&#xff0…...

ffmpeg音频分析

对一个16k 单声道音频,生成频谱图 ./ffmpeg -i input.wav -lavfi "showspectrumpics800x400:modecombined:scalelin:gain1.5" spectrum.png...

【多线程】CAS机制

目录 一. CAS的概念 二. CAS的原理 三.标准库中的CAS 四. CAS的应用 (1)原子类的使用 (2) 自旋锁的实现 五. CAS的ABA问题 一. CAS的概念 CAS(Compare And Swap)机制是一种无锁的并发控制技术&#…...