就如同那不知疲倦的接线员一般的 Win32 程序,在百分之九十九的时间里,都持续不断去应对处理各类林林总总的消息。每当你轻下鼠标、叩响键盘,那般情形下,系统便会生成与之相对应的消息来告知程序,这般表象就是消息驱动的实质所在。只有深入领会这个消息构筑的机制,才算是真正跨进 Windows 编程的门槛。
系统所设定好的数值,便是消息,它的用途旨在告知应用程序所发生之事,像鼠标左键按下这一情况对应着WM_LBUTTONDOWN呢,此宏设定的值为0x0201,每一个消息均有着明晰的含义,系统借助消息将硬件操作转变成程序能够领会的语言。
操作系统Windows界定了几百种消息,此范围自窗口创建起始直至毁坏完全覆盖,程序于接收到消息之后依据类型施行相应操作,举例而言,当点击关闭按钮后便会激发WM_CLOSE消息。这般设计使得操作系统与应用程序之间具备了统一的沟通语言。
在消息传递之时,消息被封装成为了 MSG 结构体,此结构体之中存在着五个关键的成员,窗口句柄用于明确消息要发送至何处,消息 ID 能够告知程序所发生的是何种事件,wParam 和 lParam 会携带额外的参数,另外还有消息的投递时间以及鼠标的位置。
提及拿鼠标点击这一行为,lParam的低16位里面存放着X坐标,其高16位存放着Y坐标,wParam用于标识按下了哪一个键。这些信息被整齐地进行打包,在窗口过程收到之后,直接解析便能够获取全部细节,并不需要额外进行查询。
系统维持着一个面向全局的消息队列,所有由硬件触发产生的原始消息,都首先放置于此。当鼠标驱动察觉到点击动作后,系统会将此消息从全局队列当中提取出来,依据当前处于活动状态的窗口,定位至具体的线程,进而投递到与之对应的线程消息队列里。
专属每个GUI线程的消息队列,存储着仅属于本线程窗口的消息,该队列在这个线程首次调用GDI函数之际才会被创建,可算作是按需进行分配的,应用程序借助GetMessage或者PeekMessage从这里把消息取走并处理。
系统消息所覆盖的范围是从0到1023,它被细分为窗口消息、命令消息以及控件通知这几种类型。窗口消息负责进行创建以及绘制方面的工作,命令消息主要处理菜单工具栏相关事宜,控件通知则承担按钮编辑框的交互任务,NMHDR结构体还能够携带自定义的数据,其扩展性是很强的。
定制化消息起始于1024,开发者借助WM_USER + n这种形式予以定义。此部分消息,系统并不认知,完全依靠应用程序自身去处置。常见的举措是在头文件里进行#define WM_MY_MESSAGE WM_USER+1,接着展开响应处理。
那连接着电脑的鼠标以及键盘这般组成部分引发的信息属于队列信息,它是严格依照事件、入队、拿出以及处理的次序稳步前行的。而经由SendMessage传输的消息呢是非队列消息,它会瞬间跃入窗口程序处理完毕之后才会返回,适用于那些亟需马上予以回应的情形,就像发送WM_CLOSE从而要求即刻关闭一样。
与PostMessage相反,将消息抛至队列后便会返回,全然不顾对方在何时进行处理。此种异步特性于处理跨线程通信之际时格外有用,然而需要留意的是,参数不得传递指针,这是鉴于发送之时对象有可能已被释放。
GetMessage是具有阻塞特性的函数,当消息队列呈现为空状态时,线程会处于挂起情形而不占用CPU资源。当有消息到来之际会返回非零值继而取出消息,要是收到WM_QUIT则返回零值从而结束循环。这样的设计使得程序在没有事务可处理的时候能够实现彻底的休息,而在存在任务之时能够瞬间做出响应。
PEEKMESSAGE是那种并非阻塞的类型版本,一旦没有消息便会直接返回0予以呈现。借助这一特性能够于循环之中穿插融合另外别的任务,举例而言就好像游戏在具备渲染间隙的阶段去检查消息这种情况。倘若加上PM_REMOVE这个标志,那么就能够取出并且将消息予以删除,而PM_NOREMOVE这个情况呢,则意味着只是偷偷看一眼但不会拿走消息。
当键盘被按下之际,所产生的乃是WM_KEYDOWN这一类虚拟键消息,其并不代表具体的字符。然而,TranslateMessage函数会检查当下的键盘状态,要是此状态对应的是可显示字符,那么就会生成WM_CHAR消息并将其投递至队列,而紧接着,这条新消息会在下次循环之时被取出进而予以处理。
切换键锁定、大小写状况均会被纳入考量范围,转变硬件扫描码为切实的文字录入。整个进程自行达成,开发者仅需于窗口进程中回应WM_CHAR便可获取用户所输入的字符。
SendMessage径直将消息送交窗口过程予以处理,等候返回值,假使是跨线程发送,系统会令目标线程处理完毕之后才去唤醒发送线程,这般同步机制适宜于需要获取处理结果的情景,诸如发送WM_GETTEXT去读取文本框内容。
PostMessage仅仅是专门负责进行投递操作而已,并不去处理相关事务,哪怕是目标窗口出现卡死的状况,那么也是不会对调用者造成影响的;.PostThreadMessage更是直接是发送给线程的,并不需要指定窗口句柄;接收方呢,在循环当中正常地进行GetMessage操作就能够获取到,其参数使用NULL来表示接收本线程的所有消息。
到底有没有在实际开展的开发里碰到过消息丢失、或者界面卡死这样的状况呢?欢迎于评论区去分享你那排错的经验,点赞并且收藏这篇文章,以此方便随时去查阅消息机制的那些细节之处。