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

《Windows API每日一练》6.2 客户区鼠标消息

第五章已经讲到,Windows只会把键盘消息发送到当前具有输入焦点的窗口。鼠标消息则不同:当鼠标经过窗口或在窗口内被单击,则即使该窗口是非活动窗口或不带输入焦点, 窗口过程还是会收到鼠标消息。Windows定义了 21种鼠标消息。不过,其中11种消息与 客户区无关,称为“非客户区消息”。Windows应用程序经常忽略这类消息。

本节必须掌握的知识点:

        客户区鼠标消息

        第35练:客户区鼠标消息的处理

6.2.1 客户区鼠标消息

客户区鼠标消息

当鼠标移经窗口客户区时,窗口过程接收WM_MOUSEMOVE消息。在窗口客户区内按下或释放鼠标按钮时,窗口过程接收如下表所示的消息:

按钮

按下

释放

第二次按下按钮

左键

WM_LBUTTONDOWN

WM_LBUTTONUP

WM_LBUTTONDBLCLK

中键

WM_MBUTTONDOWN

WM_MBUTTONUP

WM_MBUTTONDBLCLK

右键

WM_RBUTTONDOWN

WM_RBUTTONUP

WM_RBUTTONDBLCLK

窗口过程只对三键鼠标接收MBUTTON消息,只对双键鼠标接收RBUTTON消息。而只有当窗口类被定义成接收鼠标双击时,窗口过程才接收DBLCLK(双击)消息。

对所有这些消息来说,参数IParam包含了鼠标的位置信息,其中低位字表示x坐标, 高位字表示y坐标,它们都是相对于窗口客户区左上角的相对坐标。利用LOWORD宏和 HIWORD宏,可以获取这些坐标值:

X = LOWORD (IParam);

y = HIWORD (IParam);

参数wParam表示鼠标按钮、Shift键和Ctrl键的状态。可以利用WINUSER.H头文件中定义的位掩码来测试参数wParam。前缀MK代表“鼠标键”(mouse key)。

MK_LBUTTON       按下左键

MK_MBUTTON      按下中键

MK_RBUTTON       按下右键

MK_SHIFT             按下 Shift 键

MK_CONTROL      按下 Ctrl 键

例如,当接收到WM_LBUTTONDOWN消息时,若wparam & MK_SHIFT 的值为TRUE(非零),则表示按下鼠标左键的同时按下了 Shift键。

●处理Shift键

处理过程依赖ShiftCtrl键的逻辑处理

单键鼠标模拟双键鼠标

if (wParam & MK_SHIFT)  //按下Shift

{

    if (wParam & MK_CONTROL)

    {

        [按下Shift + Ctrl];

     }

    else{

        [只按下Shift];

    }

}else{                  //未按Shift

    if (wParam & MK_CONTROL)

    {

        [只按下Ctrl];

    }else{

        [ShiftCtrl都没被按下];

    }

}

case WM_LBUTTONDOWN:

//未按Shift时,直接处理左键

    if (!(wParam & MK_SHIFT))

    {

        [这里处理左键];

        return 0;

     }   //注意,这里没有return

    //用户按下了鼠标左键+Shift,执行以下代码,模拟右键。

case WM_RBUTTONDOWN: 

    [这里处理右键];

    return 0;

【注意】双键鼠标也是可以正常处理的。单键鼠标可以通过按住鼠标左键+Shift,来模拟鼠标右键的功能。

【注意】GetKeyState可以通过VK_LBUTTON、VK_RBUTTON、VK_SHIFT、VK_CONTROL等获取鼠标当前状态。但鼠标或键盘未被按下的键不能使用GetKeyState。只有被按下时才会报告其按下状态。(while(GetKeyState(VK_LBUTTON)>=0))是错误的代码。

●鼠标移经窗口的客户区时,Windows系统不会为鼠标经过的每个像素位置都产生 WM_MOUSEMOVE消息。程序收到的WM_MOUSEMOVE消息个数取决于鼠标硬件和窗口过程处理鼠标移动消息的速度。换言之,如果消息队列里还有未处理的 WM_MOUSEMOVE消息,Windows就不会重复向消息队列中添加该消息。试验下面这个 CONNECT程序,可以对WM_MOUSEMOVE消息的产生速度有一个全面的了解。

●若在非活动窗口的客户区内按下鼠标左键,Windows会将该窗口变为活动窗口,并向窗口过程发送WM_LBUTTONDOWN消息。当窗口过程接收到WM_LBUTTONDOWN消息时,程序就能够安全地保证该窗口是活动窗口。但是,在事先没有接收 WM_LBUTTONDOWN消息的情况下,窗口过程仍然可以接收WM_LBUTTONUP消息。 比如,当用户在其他窗口内按下鼠标,再移动到用户窗口,然后释此时就会发生这种情况。类似地,当移动鼠标到另一个窗口再释放时,前一个窗口过程在接收 WM_LBUTTONDOWN消息后,就接收不到相应的WM_LBUTTONUP消息。

前面这些规则有两个例外:

●即使鼠标位于窗口的客户区之外,窗口过程也有办法“捕获鼠标”,并且继续接收鼠标消息。本章会在后面讲述如何捕获鼠标。

●若正在显示一个系统模式消息框或系统模式对话框,则其他任何程序都不能接收鼠标消息。当系统模式消息框或对话框处于活动状态时,它们会阻止系统切换到另一个窗口。例如,关闭Windows时弹出的消息框就是一个系统模式消息框。

6.2.2 第35练:客户区鼠标消息的处理

/*---------------------------------------------------------------

035  WIN32 API 每日一练

     第35个例子CONNECT.C:客户区鼠标消息的处理

     SetPixel函数

     SetCursor函数

     ShowCursor函数

     WM_LBUTTONDOWNE消息

     WM_MOUSEMOVE消息

     WM_LBUTTONUP消息

(c) www.bcdaren.com, 2020

----------------------------------------------------------------*/

#include <windows.h>

#define MAXPOINTS 1000

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

 PSTR szCmdLine, int iCmdShow)

{

     static TCHAR szAppName[] = TEXT("Connect");

    (略)

     return msg.wParam;

}

LRESULT CALLBACK WndProc ( HWND hwnd, UINT message, WPARAM wParam,LPARAM

lParam)

{

     static POINT pt[MAXPOINTS];//鼠标经过窗口区像素点坐标数组

     static int iCount;

     HDC hdc;

     int i,j;

     PAINTSTRUCT ps;

     switch (message)

     {

     /*测试:非客户区消息

     //用于通知应用程序在非客户区(Non-Client Area)

//接收到鼠标消息时进行的命中测试(Hit Test)。

     case WM_NCHITTEST:

         //直接返回位置信息,阻止系统向所有窗口客户区和非窗口客户区发送鼠标消息

         return (LRESULT)HTNOWHERE;

     //测试:按下ALT+F、Ctrl+C等系统消息

     case WM_SYSKEYDOWN:

        //直接返回,使所有系统键盘消息失效

         return 0;*/

     //按下鼠标左键消息

     case WM_LBUTTONDOWN:    

          iCount = 0;

          InvalidateRect(hwnd,NULL,TRUE);//重绘窗口---清除背景

          return 0;

     //鼠标移动消息

     case WM_MOUSEMOVE:  

          //按下鼠标左键并且iCount小于1000

          if (wParam & MK_LBUTTON && iCount < 1000)

          {

               //填充坐标数组

               pt[iCount].x = LOWORD(lParam);

               pt[iCount++].y = HIWORD(lParam);

               hdc = GetDC(hwnd);

               //设置像素点颜色,RGB(0)黑色

               SetPixel(hdc,LOWORD(lParam),HIWORD(lParam),0);

               ReleaseDC(hwnd,hdc);

          }

          return 0;

     //松开鼠标左键消息

     case WM_LBUTTONUP

          //重新绘制窗口---不清除背景,保留WM_MOUSEMOVE里画下的点。

          InvalidateRect(hwnd,NULL,FALSE);

          return 0;

     case WM_PAINT:

          hdc = BeginPaint(hwnd,&ps);

          SetCursor(LoadCursor(NULL,IDC_WAIT));//设置鼠标形状为等待状态

          ShowCursor(TRUE);//显示鼠标

          //像素点之间画线

          for (i = 0;i < iCount - 1;i++)

          {

               for (j = 0;j < iCount - 1;j++)

               {

                    MoveToEx(hdc,pt[i].x,pt[i].y,NULL);

                    LineTo(hdc,pt[j].x,pt[j].y);

               }

          }

          ShowCursor(FALSE);//隐藏鼠标

          SetCursor(LoadCursor(NULL,IDC_ARROW));//设置鼠标位图“箭头形状”

          EndPaint(hwnd,&ps);

          return 0;

     case WM_DESTROY:

          PostQuitMessage(0);

          return 0;

     }

     return DefWindowProc(hwnd, message, wParam, lParam);

}

/***************************************************************************

SetPixel函数:指定坐标到指定的颜色设置像素

COLORREF SetPixel(

  HDC      hdc,

  int      x,  //坐标

  int      y,

  COLORREF color    //RGB颜色

);

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

SetCursor函数:设置鼠标形状

HCURSOR SetCursor(

  HCURSOR hCursor   //IDC_ARROW,IDC_WAIT

);

ShowCursor函数:显示/隐藏鼠标

int ShowCursor(

  BOOL bShow   //TRUE显示,FALSE隐藏

);

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

WM_LBUTTONDOWNE消息:当光标在窗口的客户区域中时用户按下鼠标左键时发布。

如果未捕获鼠标,则消息将发布到光标下方的窗口。否则,该消息将发布到捕获鼠标的窗口中。

参数wParam:指示各种虚拟键是否按下。此参数可以是以下一个或多个值。

MK_CONTROL  0x0008  CTRL键按下。

MK_LBUTTON  0x0001  鼠标左键按下。

MK_MBUTTON  0x0010  鼠标中键按下。

MK_RBUTTON  0x0002  鼠标右键按下。

MK_SHIFT    0x0004  SHIFT键按下。

MK_XBUTTON1 0x0020  第一个X按钮按下。

MK_XBUTTON2 0x0040  第二个X按钮按下。

lParam

低位字指定光标的x坐标。坐标相对于客户区域的左上角。

高阶字指定光标的y坐标。坐标相对于客户区域的左上角。

返回值

如果应用程序处理此消息,则应返回零。

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

WM_MOUSEMOVE消息:光标移动时张贴到窗口。如果未捕获鼠标,则消息将发布到包含光标的窗口中。否则,该消息将发布到捕获鼠标的窗口中。

参数与WM_LBUTTONDOWNE消息相同

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

WM_LBUTTONUP消息:当光标在窗口的客户区域中时,用户释放鼠标左键时发布。

如果未捕获鼠标,则消息将发布到光标下方的窗口。否则,该消息将发布到捕获鼠标的窗口中。

参数与WM_LBUTTONDOWNE消息相同

*/

       运行结果:

图6-1 客户区鼠标消息

 

总结

●实例操作方法:

1.第一种——在客户区按下左键,略微移动,再松开左键。

2.第二种——在客户区按下左键,快速移动鼠标。

●己知的问题:在客户区外释放左键,Connnect不会连接这些点,因为没收到WM_LBUTTONUP消息。

●该程序较耗时,绘制时,鼠标变沙漏形,处理WM_PAINT完后回原来的状态。用SetCursor来切换鼠标。ShowCursor隐藏或显示鼠标指针。

●窗口过程:

1.实例CONNECT.C处理了三个鼠标消息。

WM_LBUTTONDOWNE消息:按下鼠标左键时,调用InvalidateRect函数清除背景,重绘窗口。

WM_MOUSEMOVE消息:移动鼠标时,采集不超过1000个鼠标移动坐标点,保存在pt数组中,然后使用SetPixel函数绘制坐标点(系统默认黑色画笔)。

WM_LBUTTONUP消息:松开鼠标左键时,重绘窗口,但是并不清除背景。

2.处理WM_PAINT消息时,CONNECT程序需要耗费一定的时间来绘制直线,因此鼠标指针会变成等待位图。调用SetCursor函数,加载并设置鼠标位图为等待位图,显示鼠标位图。接着使用双循环将所有坐标点连接起来。然后再恢复原鼠标位图。

3.在用户释放左键时,如果鼠标指针已经移出客户区,CONNECT程序就不会连接这些点, 因为程序没有接收到WM_LBUTTONUP消息。此时如果再将鼠标移入客户区,并按下左键,CONNECT程序就会清空客户区。如果想在客户区外释放鼠标,并继续设计图形,就可以在客户区外按下鼠标的左键,再将鼠标移入客户区。

4.动手实验:处理WM_NCHITTEST消息时可以直接返回鼠标位置信息,阻止系统向所有窗口客户区和非窗口客户区发送鼠标消息。

处理WM_SYSKEYDOWN消息时,可以让所有系统键盘消息失效。

下一节我们讲述如何在非窗口客户区捕捉鼠标消息。

相关文章:

《Windows API每日一练》6.2 客户区鼠标消息

第五章已经讲到&#xff0c;Windows只会把键盘消息发送到当前具有输入焦点的窗口。鼠标消息则不同&#xff1a;当鼠标经过窗口或在窗口内被单击&#xff0c;则即使该窗口是非活动窗口或不带输入焦点&#xff0c; 窗口过程还是会收到鼠标消息。Windows定义了 21种鼠标消息。不过…...

体验升级:扫描全能王智能高清滤镜2.0全面测评

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…...

【JVM系列】JVM调优

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

Linux基础 - Postfix 与 Dovecot 部署邮件系统

目录 零. 简介 一. 部署 二. 设置用户别名信箱 三. Linux 邮件客户端 零. 简介 Postfix 和 Dovecot 是在 Linux 系统中常用于部署邮件系统的两个重要组件。 Postfix 是一种邮件传输代理&#xff08;MTA&#xff09;&#xff0c;主要负责接收、转发和发送邮件。它具有高性能…...

Qt的安装

一、Qt安装 下载地址&#xff1a;https://download.qt.io/archive/qt/ opencv下载安装 下载地址&#xff1a;https://opencv.org/releases/ 陈年旧文&#xff0c;没有下文&#xff0c;以此纪念。。。。。...

ThreeJS-3D教学十二:ShaderMaterial

一、首先 Shader 是做什么的 Shader 可以自定义每个顶点、每个片元/像素如何显示&#xff0c;而控制顶点和片元显示是通过设置 vertexShader 顶点着色器和 fragmentShader 片元着色器&#xff0c;这两个着色器用在 ShaderMaterial 和 RawShaderMaterial 材质上。 我们先看一个例…...

计算机网络面试TCP篇之TCP三次握手与四次挥手

TCP 三次握手与四次挥手面试题 任 TCP 虐我千百遍&#xff0c;我仍待 TCP 如初恋。 巨巨巨巨长的提纲&#xff0c;发车&#xff01;发车&#xff01; PS&#xff1a;本次文章不涉及 TCP 流量控制、拥塞控制、可靠性传输等方面知识&#xff0c;这些知识在这篇&#xff1a; TCP …...

Python-数据分析组合可视化实例图【附完整源码】

数据分析组合可视化实例图 开篇&#xff1a;应女朋友的要求&#xff0c;于是写下了这篇详细的数据可视化代码及完整注释 一&#xff1a;柱状图、折线图横向组合网格布局 本段代码使用了pyecharts库来创建一个包含多个图表&#xff08;柱状图、折线图&#xff09;和网格布局的…...

【JavaEE】Spring Web MVC详解

一.基本概念. 1.什么是Spring Web MVC? 官方链接: https://docs.spring.io/spring-framework/reference/web/webmvc.html Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning…...

docker安装rocketMq5x以上的版本

1.背景 安装RocketMQ 5.x以上的版本主要是因为新版本引入了许多性能优化、新功能以及对已有特性的增强&#xff0c;这些改进可以帮助提升消息队列系统的稳定性和效率。 1.性能提升&#xff1a;RocketMQ 5.x版本通常包括了对消息处理速度、吞吐量和延迟的优化&#xff0c;使得系…...

【Spring】DAO 和 Repository 的区别

DAO 和 Repository 的区别 1.概述2.DAO 模式2.1 User2.2 UserDao2.3 UserDaoImpl 3.Repository 模式3.1 UserRepository3.2 UserRepositoryImpl 4.具有多个 DAO 的 Repository 模式4.1 Tweet4.2 TweetDao 和 TweetDaoImpl4.3 增强 User 域4.4 UserRepositoryImpl 5.比较两种模式…...

高阶面试-秒杀系统的设计

场景 特价商品如茅台&#xff0c;在8月1日22点10分0秒开始秒杀 平台用户量&#xff1a;几千万&#xff0c;预计几十万用户感兴趣 需求 临时性的活动&#xff0c;不要太大技术改动 原则 商品不能超卖下单成功的订单不能丢失服务器和数据库不能崩溃尽量不让机器人抢走商品 …...

四十五、 证券基金业数据出境有无特别规范需要注意?

证券基金业数据合规除应遵守本《实务问答》前述的各项通用规定外&#xff0c;还应注意中国证券监督管理委员会等其他机构发布的相关规范。其中&#xff0c;与数据出境相关的主要包括《证券期货业数据分类分级指引》&#xff08;JR/T 0158—2018&#xff0c;2018年 9月 27日实施…...

02.Linux下安装FFmpeg

目录 一、下载FFmpeg的编译源码 二、编译源码 三、ffmpeg工具结构解析 1、bin目录 2、include库 3、lib库 四、注意事项 五、可能出现的一些问题 1、某些工具未安装/版本过久 2、缺少pkg-config工具 3、缺少ffmplay FFmpeg 是一个开源的跨平台音视频处理工具集&…...

华为RH2288H V2服务器,远程端口安装Linux操作系统

1、管理口 每台服务器的管理口不一样的&#xff0c;假如我的管理IP地址为&#xff1a;192.168.111.201 使用网线&#xff0c;将管理口和自己电脑连接起来&#xff0c;自己ip地址设置成和管理ip同一网段。 使用 ie 浏览器&#xff0c;如果是Edge&#xff0c;必须在Internet Exp…...

JS在线加密简述

JS在线加密&#xff0c;是指&#xff1a;在线进行JS代码混淆加密。通过混淆、压缩、加密等手段&#xff0c;使得JS源代码难以阅读和理解。从而可以有效防止代码被盗用或抄袭&#xff0c;保护开发者的知识产权和劳动成果。常用的JS在线加密网站有&#xff1a;JShaman、JS-Obfusc…...

理想汽车提出3DRealCar:首个大规模3D真实汽车数据集

理想提出3DRealCar&#xff0c;这是第一个大规模 3D 实车数据集&#xff0c;包含 2500 辆在真实场景中拍摄的汽车。我们希望 3DRealCar 可以成为促进汽车相关任务的宝贵资源。 理想汽车提出3DRealCar&#xff1a;首个大规模3D真实汽车数据集! 我们精心策划的高质量3DRealCar数…...

HTML5文旅文化旅游网站模板源码

文章目录 1.设计来源文旅宣传1.1 登录界面演示1.2 注册界面演示1.3 首页界面演示1.4 文旅之行界面演示1.5 文旅之行文章内容界面演示1.6 关于我们界面演示1.7 文旅博客界面演示1.8 文旅博客文章内容界面演示1.9 联系我们界面演示 2.效果和源码2.1 动态效果2.2 源代码2.3 源码目…...

山东大学多核并行2024年回忆版

2024.6.13回忆版 矩阵向量乘不可整除代码 集合通信与点对点通信的区别 块划分、循环划分、循环块划分&#xff08;14个向量&#xff0c;4个进程&#xff09; 按行访问还是按列访问快 SISD系统问题 循环依赖问题 问题&#xff1a;为什么不能对这个循环并行化&#xff0…...

CentOS 7 上搭建 JavaEE 环境

CentOS 7 上搭建 JavaEE 环境 安装 Java 环境 1&#xff09;检查系统中是否已安装 Java java -version如果未安装&#xff0c;将返回提示信息。 2&#xff09;安装 Java 8 sudo yum install java-1.8.0-openjdk3&#xff09;配置 Java 环境变量&#xff0c;编辑 /etc/prof…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

多模态大语言模型arxiv论文略读(108)

CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题&#xff1a;CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者&#xff1a;Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?

现有的 Redis 分布式锁库&#xff08;如 Redisson&#xff09;相比于开发者自己基于 Redis 命令&#xff08;如 SETNX, EXPIRE, DEL&#xff09;手动实现分布式锁&#xff0c;提供了巨大的便利性和健壮性。主要体现在以下几个方面&#xff1a; 原子性保证 (Atomicity)&#xff…...