windows SDK编程 --- 消息之键盘消息(4)

前置知识

一、 键盘消息

在Windows操作系统中,键盘消息是用来通知应用程序有关键盘输入事件的一种机制。当用户在键盘上进行操作,比如按键或释放键时,Windows会生成相应的消息并发送给处理这些输入的应用程序。这些消息对于开发图形用户界面(GUI)应用程序至关重要,因为它们允许应用程序响应用户的输入。

下面是几种主要的键盘消息及其用途的详细说明:

  1. WM_KEYDOWN
描述:
当用户按下一个键盘键(并未完全释放)时发送此消息给拥有焦点的窗口。
参数:
wParam:虚拟键码。
lParam:提供了有关按键的更多信息,如重复次数、扫描码、ALT键是否被按下等。
lParam 参数的详细位分布:
0-15位:重复计数(Repeat count)。这表示自从用户开始按下键以来,由于按键保持不放,相同的消息被自动重复发送的次数。这个计数不是累积的,每次消息发送时都是独立的计数。
16-23位:扫描码(Scan code)。这是键盘硬件产生的代码,用于识别物理键。
24位:扩展键标志(Extended-key flag)。如果是扩展键(如功能键、方向键等),该位为125-28位:保留,未使用。
29位:上下文代码。对于 WM_KEYDOWN 消息,这个值始终为030位:上一个键状态(Previous key state)。如果在发送此消息之前键是未按下的,则此位为1;如果键已经是按下状态,则此位为031位:转换状态(Transition state)。对于 WM_KEYDOWN,该位总是为0;对于 WM_KEYUP,该位为1
  1. WM_KEYUP
描述:
当用户释放一个键盘键时发送此消息。
参数:
wParam:虚拟键码。
lParam:同样包含键的重复次数、扫描码、是否有ALT键等信息。
  1. WM_CHAR
描述:
用于处理字符输入,当按下能产生字符的键时,通过 TranslateMessage 函数生成。例如,按下 A 键时,如果启用了大写锁定或者同时按住了Shift键,可能会发送 WM_CHAR 消息并携带字符 ‘A’。
参数:
wParam:字符的ASCII或Unicode值。
lParam:与 WM_KEYDOWN 和 WM_KEYUP 相同,提供了关于按键事件的更多细节。
  1. WM_SYSKEYDOWN 和 WM_SYSKEYUP
描述:
这些消息类似于 WM_KEYDOWN 和 WM_KEYUP,但它们用于处理系统键(如Alt和F10)。
参数:
wParam:虚拟键码。
lParam:包含与普通键盘消息相同的额外信息。

二、什么是虚拟键码

虚拟键码是Windows操作系统定义的一组常数,用于表示键盘上的每一个按键。这些键码可以让开发者在编程时准确地识别用户按下或释放的具体键,无论键盘布局和硬件如何。虚拟键码在处理键盘输入提供了一个硬件无关的方法来识别键盘动作。简而言之,虚拟键码就是对键盘按键的一种编程上的抽象表示。wParam 参数会包含被按下或释放键的虚拟键码。

VK_A:字母 'A' 键(值为 0x41)
VK_0:数字 '0' 键(值为 0x30)
VK_ESCAPE:Esc键(值为 0x1B)
VK_RETURN:回车键(值为 0x0D)
VK_LEFT:左方向键(值为 0x25)
VK_RIGHT:右方向键(值为 0x27)
VK_UP:上方向键(值为 0x26)
VK_DOWN:下方向键(值为 0x28

三、什么是扫描码?扫描码和虚拟键码的关系?

在Windows编程中,处理键盘事件如 WM_KEYDOWN 时,可以通过消息的 lParam 获取扫描码,而 wParam 提供虚拟键码。这允许程序同时利用键的物理位置(扫描码)和逻辑功能(虚拟键码)来响应用户的键盘操作。

键盘扫描码(Scan Code)

键盘扫描码是键盘硬件为每次按键生成的原始代码,表示按下的是键盘上的哪一个物理键。这些代码通常是硬件级的,与操作系统和键盘布局无关。扫描码的主要作用是在最底层—即硬件和操作系统的界面—提供键位信息。

  • 特点:扫描码直接来自键盘,不受键盘语言布局的影响。
  • 用途:用于在低级别(如驱动程序或操作系统核心)处理键盘输入。

虚拟键码(Virtual-Key Code)

虚拟键码是操作系统级的抽象,它将扫描码转换为一个标准化的代码,这反映了按键的“功能”,而不仅仅是位置。例如,无论在哪种键盘布局中,字母“A”的虚拟键码都是相同的,即使其物理位置可能不同。

  • 特点:虚拟键码依赖于键盘布局,因为它反映了按键的逻辑功能(如字符、命令等)。
  • 用途:用于编写应用程序时处理更高级的输入(如文字输入、控制命令等)。

两者的区别

  • 来源:扫描码来自键盘硬件;虚拟键码由操作系统提供。
  • 依赖性:扫描码与键盘布局无关,与物理键位置直接相关;虚拟键码依赖于键盘布局,反映键的逻辑功能。
  • 用途:扫描码通常在较低的系统级别(如操作系统或驱动程序)使用,用于识别物理键位;虚拟键码在更高级的应用程序中使用,用于处理具体的键盘功能,如文本输入或用户界面控制。

三、快捷键Shortcut Keys)和热键(Hotkeys)有什么区别

快捷键(Shortcut Keys)

  • 快捷键通常是指在特定应用程序内部使用的键盘组合,用于执行应用程序的特定功能。这些快捷键只在该应用程序具有焦点时有效。例如,许多应用程序都使用 Ctrl + S 作为保存操作的快捷键。
  • 上下文依赖:快捷键的作用依赖于当前应用程序的状态和哪个窗口或控件有焦点。
  • 实现方式:在应用程序的消息处理逻辑中捕捉和处理特定的 WM_KEYDOWN 或 WM_KEYUP 消息来实现快捷键功能。

热键(Hotkeys)

  • 热键是系统级的键盘快捷键,它们在整个操作系统中都是有效的,无论当前哪个应用程序有焦点。热键通常用于操作系统功能或某些特定应用程序的功能,这些功能需要从任何地方快速访问。
  • 全局作用域:热键无论在哪个应用程序中都能被触发
  • 注册使用:通过使用Windows API的 RegisterHotKey 函数来设置。这个函数允许你定义一个热键和一个与之关联的消息,当热键被按下时,这个消息将被发送到指定的窗口处理。

示例代码

#include <windows.h>//5.消息处理
LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam
)
{// 根据消息类型进行分支处理switch (msg){case WM_CREATE:{//注册热键RegisterHotKey(hwnd, 5566, MOD_CONTROL, VK_F1);return 0;}//键盘消息case WM_KEYDOWN:{//获得键盘状态BYTE KeyState[256];if (GetKeyboardState(KeyState)==0){return TRUE; }//键盘扫描码BYTE ScanCode = (int)lParam >> 16 && 0xff;WORD ch;//将键盘扫描码转换成Ascii码ToAscii(wParam, ScanCode, KeyState, &ch, 0);char buffer[100];wsprintf(buffer, "你按下了:%c\n", ch);OutputDebugString(buffer);return 0;}case WM_CHAR:{char buffer[100];wsprintf(buffer, "你按下了:%c\n", wParam);OutputDebugString(buffer);return 0;}case WM_KEYUP:{return 0;}//热键消息case WM_HOTKEY:{MessageBox(NULL, "HotKey!", "WM_HOTKEY", MB_OK);return 0;}case WM_CLOSE:{//询问是否关闭窗口int id = MessageBox(NULL, "你确定关闭窗口吗", "WM_CLOSE", MB_YESNO);if (id == IDYES){DestroyWindow(hwnd);}return 0;}case WM_DESTROY:{//注销热键UnregisterHotKey(hwnd,5566);PostQuitMessage(0); return 0;}}return DefWindowProc(hwnd, msg, wParam, lParam);}int WINAPI WinMain(HINSTANCE hInstance,     // 当前实例的句柄HINSTANCE hPrevInstance, // 前一个实例的句柄,现在总是为 NULLLPSTR lpCmdLine,         // 命令行参数的字符串int nCmdShow             // 指示程序窗口应如何被显示
)
{//1.注册窗口char MyWindowClassName[] = "MyWindowClass";         //窗口类名WNDCLASSEX wc = { 0 };wc.cbSize = sizeof(WNDCLASSEX);wc.style = CS_VREDRAW | CS_HREDRAW;     //wc.lpfnWndProc = WndProc;    //窗口过程函数(窗口回调函数->处理消息)wc.hInstance = hInstance;wc.hIcon = NULL;             //图标wc.hCursor = NULL;          //光标wc.hbrBackground = CreateSolidBrush(RGB(0, 255, 0));    //窗口背景颜色刷子wc.lpszMenuName = NULL;     //菜单名称wc.lpszClassName = MyWindowClassName;    //窗口类名if (RegisterClassEx(&wc) == 0){return 0;}//2.创建窗口char MyWindowName[] = "MyWindowClass";         //窗口名称HWND hwnd = CreateWindowEx(0,MyWindowClassName,MyWindowName,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);if (hwnd == NULL){return 0;}//3.显示更新窗口ShowWindow(hwnd, SW_SHOWNORMAL);UpdateWindow(hwnd);//4.消息循环(消息队列)MSG msg = {};while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/829496.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ElasticSearch教程入门到精通——第二部分(基于ELK技术栈elasticsearch 7.x+8.x新特性)

ElasticSearch教程入门到精通——第二部分&#xff08;基于ELK技术栈elasticsearch 7.x8.x新特性&#xff09; 1. JavaAPI-环境准备1.1 新建Maven工程——添加依赖1.2 HelloElasticsearch 2. 索引2.1 索引——创建2.2 索引——查询2.3 索引——删除 3. 文档3.1 文档——重构3.2…

react,Chart

一、基础图&#xff1a;https://ant-design-charts.antgroup.com/ Ant Design Charts 1. 首先要下载ant-design/charts&#xff0c;然后在页面中添加如下柱状图代码: import React from react; import { Column } from ant-design/chartsconst DemoColumn: React.FC () …

百度智能云千帆 ModelBuilder 技术实践系列:通过 SDK 快速构建并发布垂域模型

​百度智能云千帆大模型平台&#xff08;百度智能云千帆大模型平台 ModelBuilder&#xff09;作为面向企业开发者的一站式大模型开发平台&#xff0c;自上线以来受到了广大开发者、企业的关注。至今已经上线收纳了超过 70 种预置模型服务&#xff0c;用户可以快速的调用&#x…

深入理解冯诺依曼体系结构

文章目录 冯诺依曼体系结构概念冯诺依曼体系结构的优势冯诺依曼体系结构的现实体现 冯诺依曼体系结构概念 冯诺依曼体系结构也称普林斯顿结构&#xff0c;是现代计算机发展的基础。它的主要特点是“程序存储&#xff0c;共享数据&#xff0c;顺序执行”&#xff0c;即程序指令和…

代码随想录算法训练营Day10 | 232.用栈实现队列、225. 用队列实现栈

232.用栈实现队列 题目&#xff1a;请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a; void push(int x) 将元素 x 推到队列的末尾int pop() 从队列的开头移除…

【leetcode面试经典150题】75. 二叉树展开为链表(C++)

【leetcode面试经典150题】专栏系列将为准备暑期实习生以及秋招的同学们提高在面试时的经典面试算法题的思路和想法。本专栏将以一题多解和精简算法思路为主&#xff0c;题解使用C语言。&#xff08;若有使用其他语言的同学也可了解题解思路&#xff0c;本质上语法内容一致&…

修改Docker容器内文件的三种方式

说明&#xff1a;本文介绍修改Docker容器内文件的三种方式 方式一&#xff1a;直接修改 敲下面的命令&#xff0c;进入Docker容器&#xff0c;如mysql docker exec -it mysql /bin/bash修改mysql的配置文件&#xff0c;/etc/my.cnf vim /etc/my.cnf如下&#xff0c;如果vim…

【Hadoop】-Apache Hive使用语法与概念原理[15]

一、数据库操作 创建数据库 create database if not exists myhive; 使用数据库 use myhive; 查看数据库详细信息 desc database myhive; 数据库本质上就是在HDFS之上的文件夹。 默认数据库的存放路径是HDFS的&#xff1a;/user/hive/warehouse内 创建数据库并指定hdfs…

attempt to compare nil with number -- 黑马点评出现问题

问题情况 : 主要问题 : 调用lua执行redis时&#xff0c;有一个值会接受nil&#xff08;因为redis中没有该数据&#xff09;或者数值&#xff0c;当该值为nil时执行报错&#xff0c;因为会用到将该值与其他数字比较&#xff0c;故报错attempt to compare nil with number 当然…

生成完美口型同步的 AI 代言人视频(及其实现原理详解)

目录 什么是Heygen? Heygen注册 Video Translation&#xff08;视频翻译 完美口型同步&#xff09; 实现原理详解 视频翻译部分 完美口型同步部分 什么是Heygen? Heygen是一款在线工具&#xff0c;可帮助您生成具有完美口型同步的 AI 代言人视频。 Heygen注册 https:…

关于springboot内置tomcat最大请求数配置的一些问题

前言 springboot内置了tomcat。那么一个springboot web应用&#xff0c;最大的请求链接数是多少呢&#xff1f;很早以前就知道这个是有个配置&#xff0c;需要的时候&#xff0c;百度一下即可。但&#xff0c;事实并非如此&#xff0c;有几个问题我想大多数人还真不知道。比如…

前端学习<四>JavaScript——54-原型链

常见概念 构造函数 构造函数-扩展 原型规则和示例 原型链 instanceof 构造函数 任何一个函数都可以被 new&#xff0c;new 了之后&#xff0c;就成了构造方法。 如下&#xff1a; function Foo(name, age) {this.name name;this.age age;//retrun this; //默认有这…

大型语言模型高效推理综述

论文地址&#xff1a;2404.14294.pdf (arxiv.org) 大型语言模型&#xff08;LLMs&#xff09;由于在各种任务中的卓越表现而受到广泛关注。然而&#xff0c;LLM推理的大量计算和内存需求给资源受限的部署场景带来了挑战。该领域的努力已经朝着开发旨在提高LLM推理效率的技术方…

C语言递归刷题(一)

目录 走台阶题目思路代码 西格玛题目思路代码 用函数实现数的阶乘题目思路代码 digit题目思路代码 Hermite多项式题目思路代码 排列数题目思路代码 逆序输出题目思路代码 结语 走台阶 题目 描述 小乐乐上课需要走n阶台阶&#xff0c;因为他腿比较长&#xff0c;所以每次可以选…

挑战特斯拉?深蓝汽车与华为强强联手

作为中国乃至全球汽车行业的盛宴&#xff0c;4月25日在中国国家展览中心揭幕的2024北京国际车展&#xff0c;吸引了无数企业行业人士的关注。 而就在车展开幕当天&#xff0c;深蓝汽车发布会就爆出了一个大新闻&#xff1a;深蓝汽车将携手华为&#xff0c;打造比特斯拉更好的智…

【开发问题记录】启动某个服务时请求失败(docker-componse创建容器时IP参数不正确)

问题记录 一、问题描述1.1 产生原因1.2 产生问题 二、问题解决2.1 找到自己的docker-compose.yml文件2.2 重新编辑docker-compose.yml文件2.3 通过docker-componse重新运行docker-compose.yml文件2.4 重新启动docker容器2.5 查看seata信息 一、问题描述 1.1 产生原因 因为我是…

FPGA 以太网通信UDP通信环回

1 实验任务 上位机通过网口调试助手发送数据给 FPGA &#xff0c; FPGA 通过 PL 端以太网接口接收数据并将接收到的数据发送给上位机&#xff0c;完成以太网 UDP 数据的环回。 2 系统设计 系统时钟经过PLL时钟模块后&#xff0c;生成了两种不同频率和相位的时钟信号&#…

Python 面向对象——6.封装

本章学习链接如下&#xff1a; Python 面向对象——1.基本概念 Python 面向对象——2.类与对象实例属性补充解释&#xff0c;self的作用等 Python 面向对象——3.实例方法&#xff0c;类方法与静态方法 Python 面向对象——4.继承 Python 面向对象——5.多态 1. 封装的基…

unity cinemachine相机 (案例 跟随角色移动)

安装相机包 打开包管理工具 在 unity registry 搜索cinemachine 会在maincamera中生成一个组件cinemachineBrain 只能通过虚拟相机操控 主相机 虚拟相机的参数 案例 1.固定相机效果 位置 在固定的地方 默认的模式 2.相机跟随人物效果 焦距设置 20 跟随设置 把playere…

使用Tortoise 创建远程分支

1。首先创建本地分支branch1&#xff0c;右键tortoise git->创建分支&#xff0c;输入分支名称branch1&#xff0c;确定。 2。右键tortoise git->推送&#xff0c;按下图设置&#xff0c;确定&#xff0c;git会判断远程有没有分支branch1&#xff0c;如果没有会自动创建…