Windows消息,消息循环的处理,消息队列,键盘消息,鼠标消息,定时器消息(一)

简介: Windows消息,消息循环的处理,消息队列,键盘消息,鼠标消息,定时器消息

上一章节中我们带大家编写了第一个Windows程序,并且带大家学习了注册窗口,创建窗口,这一章中我们来学习Windows消息,学习对消息循环处理的原理,并且带领大家学习一些常见的消息。

一.消息基础

1.消息概念及其作用

在Windows平台下,消息组成:

  • 窗口句柄
  • 消息ID
  • 消息的两个附加信息
  • 消息产生的时间
  • 产生消息时,鼠标的位置
    我们来看看微软定义的消息结构到底是怎样的:
typedef struct tagMSG{
  HWND hwnd;         //接收消息的窗口句柄
  UINT message;      //消息类型(消息标识符)
  WPARAM wParam;     //关于消息的附加消息
  LPARAM lParam;     //关于消息的附加消息
  DWORD time;        //消息的产生时间
  POINT pt;          //发布消息时的光标位置(以屏幕坐标系表示)
}

MSDN官方文档msg结构

2.消息的作用

当系统通知窗口工作时,就采用消息的方式派发给窗口的消息处理函数,以完成窗口的工作。

3.派发消息的过程

  • 1.根据消息的窗口句柄,找到相对应的窗口
  • 2.找到保存窗口数据的内存
  • 3.找到消息处理函数
  • 4.Wndproc(…){
    回到自己的代码(处理消息);
    }

4.回调函数(窗口处理函数)

每个窗口都必须有回调函数。在应用程序中定义的回调函数,用于处理发送到窗口的消息WNDPROC类型定义指向此回调函数的指针,wndproc名称时应用程序中定义的函数名称的占位符。

我们来看看官方定义的窗口处理函数的原型:

LRESULT CALLBACK WindowProc(
  HWND hwnd;      //窗口句柄
  UINT uMsg;      //消息ID
  WPARAM wParam;  //消息附加参数
  LPARAM lParam;  //消息附加参数
){......};

MSDN官方文件解释WNDPROC函数

在这里为大家解释:
LRESULT是程序返回到Windows的整数数值。它包含程序对特定消息的响应。此值的含义取决于消息代码。
CALLBACK是函数的调用约定。

当系统通知窗口时,会调用窗口处理函数,同时将消息ID等附加消息传给窗口处理函数,在窗口处理函数中,不处理的消息,使用缺省窗口处理函数(如DefWindowProc。

5.浅谈消息相关函数

GetMessage函数–获取本进程的消息

Bool GetMessage(
  LPMSG lpMsg;       //指向MSG结构的指针,该结构从线程的消息队列中结构消息
  HWND hwnd;         //要检索其消息的窗口句柄,窗口必须属于当前线程
  UINT wMsgFilterMin;  //要检索的最低消息值的整数值
  UINT wMsgFilterMax;  //要检索的最高消息值的整数值
);

TranslateMessage函数–翻译消息

BOOL TranslateMessage(
  const MSG* lpMsg;
);

二.消息循环处理的原理

1.消息循环处理的阻塞

GetMessage函数: 从系统中获取消息,将消息从系统中移除,阻塞函数。当无系统消息时,等候下一条消息。

PeekMessage函数: 以查看的方式从系统中获取消息,可以不将消息从系统中移除,非阻塞函数。当系统无消息时,返回FALSE,继续执行后续代码。

BOOL PeekMessage(
  LPMSG lpMsg;       //指向接收信息的MSG结构的指针
  HWND hwnd;         //要检索其消息的窗口句柄,窗口必须属于当前线程
  UINT wMsgFilterMin; //要检查的消息范围的第一条消息的值
  uint wMsgFilterMax; //要检查的消息范围的最后一条消息的值
  UINT wRemoveMsg;  //指定如何处理消息(删除/不删除消息等)
}

MSDN官方文档解释PeekMessage函数

学习了PeekMessage函数之后我们来看看我们在上一章中写的消息循环的代码:

MSG msg;
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd,NULL, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (int) msg.wParam;
}

那么我们就会发现,我们在消息循环中只使用GetMessage函数的话,那么效率就会很低,因为GetMessage函数经常阻塞。在这里我们学习了PeekMessage函数之后,我们就可以将PeekMessage函数当作侦察兵,先让他去看看有没有消息,如果有消息的话,我们就可以在再去GetMessage等函数。

看看我们优化后的代码:

while (1) {
    if(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
      if (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }
      else {
        return 0;
      }
    }
    else {
      //在空闲的时候,我们想让它做什么就可以填在这里
    }
  }

2.在应用程序中调出控制台调试程序

在我们编写控制台程序的时候,我们可以随时输出来调试程序,那么我们在编写Windows程序的时候,我们无法在程序中直接调试程序,那么我们就需要调出控制台来调试我们的程序,在这里给出在我们编写的Windows程序中调出控制台来调试代码的方法:

  • 首先,我们需要定义一个全局变量(HANDLE)类型,用于接收标准输出句柄
  • 使用AllocConsole()函数在Windows程序中增加DOS窗口
  • 使用GetstdHandle函数接收标准输出句柄,并且用之前定义的HANDLE类型的全局变量接收
    使用示例:
HANDLE g_hOutput = 0;
...
AllocConsole();
g_hOutput = GetstdHandle(STD_OUTPUT_HANDLE);

其中,sprintf函数的作用是:将设置格式的数据写入字符串。

我们来看看作用效果:

3.发送消息

SendMessage函数:发送消息,会等候消息的处理结果

LRESULT SendMessage(
  HWND hWnd;      //窗口的过程句柄将接收消息
  UINT Msg;       //要发送的消息
  WPARAM wParam;  //其他的消息特定信息
  LPARAM lParem;  //其他的消息特定信息
);

返回值(LRESULT类型):返回值指定消息处理的结果,这取决于发送的消息。

MSDN官方文档解释SendMessage函数

PostMessage函数:投递消息,消息发出后立刻返回,不等候消息的处理结果

BOOL PostMessage(
  HWND hWnd;             //窗口的句柄,窗口过程是接收消息
  UINT Msg;              //要发布的消息
  WPARAM wParam;         //其他的消息特定信息
  LPARAM lParam;         //其他的消息特定消息
);

MSDN官方文档解释PostMessage函数

这里关于两个发送消息函数的不同,大家可以看文档,本章后面的消息队列也会从实操角度讲到。

4.消息分类

  • 系统消息–ID范围(0~0x03FF)(1024个)
  • 由系统定义的消息,可以在程序中直接使用
  • 用户自定义消息–ID范围(ox400~0x7FFF)(31743个)
  • 由用户自己定义,满足用户自己的需求,由用户自己发出消息,自己处理
  • 自定义消息宏:WM_USER(0x400)




相关文章
|
Shell 开发者 Windows
windows 10中将任意程序添加进鼠标右键菜单里面
windows 10中将任意程序添加进鼠标右键菜单的详细步骤
223 0
windows 10中将任意程序添加进鼠标右键菜单里面
|
6月前
|
人工智能 机器人 测试技术
【windows批处理batch】.bat文件循环判断语句
【windows批处理batch】.bat文件循环判断语句
|
1月前
|
Windows
Windows 11 鼠标右键可选择 cmd 命令行选项
Windows 11 鼠标右键可选择 cmd 命令行选项
40 0
windows10为何鼠标右键失灵,桌面右键一直转圈的解决
windows10为何鼠标右键失灵,桌面右键一直转圈的解决
1088 0
|
Windows
【windows批处理batch】.bat文件循环判断语句
【windows批处理batch】.bat文件循环判断语句
430 0
|
消息中间件 Windows
Windows消息,消息循环的处理,消息队列,键盘消息,鼠标消息,定时器消息(四)
Windows消息,消息循环的处理,消息队列,键盘消息,鼠标消息,定时器消息
|
消息中间件 Windows
Windows消息,消息循环的处理,消息队列,键盘消息,鼠标消息,定时器消息(三)
Windows消息,消息循环的处理,消息队列,键盘消息,鼠标消息,定时器消息(三)
|
4月前
|
消息中间件 C语言 RocketMQ
消息队列 MQ操作报错合集之出现"Connection reset by peer"的错误,该如何处理
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
4月前
|
消息中间件 Java C语言
消息队列 MQ使用问题之在使用C++客户端和GBase的ESQL进行编译时出现core dump,该怎么办
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
10天前
|
消息中间件 存储 Kafka
MQ 消息队列核心原理,12 条最全面总结!
本文总结了消息队列的12个核心原理,涵盖消息顺序性、ACK机制、持久化及高可用性等内容。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。