这一块一直是一知半解,目前就正好找个机会好好巩固下。

先上个参考链接:

http://www.cnblogs.com/qinfengxiaoyue/archive/2013/02/12/2910614.html

感谢分享…

 

然后上一个相关链接:可以先读读这个。

【Windows核心编程】窗口消息

 

接下来是正文部分:

 

1. 系统消息队列和应用程序消息队列

Windows系统有自己的消息队列。

系统给每一个应用程序建立一个消息队列,这个消息队列属于建立窗口的线程。

应用程序消息队列用来存放该程序可能的各种窗口消息。

建立窗口的线程必须是为窗口处理各种消息的线程。

 

2. 消息循环

应用程序中有一段代码,称为消息循环代码。代码在如下链接。

Win32标准窗口程序讲解

WinMain函数的最后一段就是消息循环代码,简化版代码如下:

这里要讲解 GetMessage 、 TranslateMessage、 DispatchMessage 这三个函数做了什么。

Windows为当前运行的应用程序维护一个消息队列,当发生了输入事件之后,Windows会将事件转换成一个消息,然后将消息放到应用程序的消息队列中去。

GetMessage函数的作用是从线程的消息队列中取出消息,以方便对它做一系列的预处理。

  • 这个函数接收一定范围内的的消息值。
  • 不接收属于其他线程或应用程序的消息。
  • 获取消息成功后,线程将从消息队列中删除该消息。
  • 函数会一直等待直到有消息到来才有返回值。

 

TranslateMessage函数的作用是将虚拟键消息转换为字符消息。

  • 该函数将虚拟键消息转换为字符消息。
  • 字符消息送到调用线程的消息队列里。

下一次调用GetMessage的时候,就可以得到转换的字符消息了。

返回值相关:

  1. 如果消息被转换(即,字符消息被送到线程的消息队列中),返回非零值。
  2. 如果消息是 WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, 或 WM_SYSKEYUP,返回非零值,不考虑转换。
  3. 如果消息没有转换(即,字符消息没被送到线程的消息队列中),返回值是零。

其他信息:

TranslateMessage函数不修改由参数lpMsg指向的消息的内容。

TranslateMessage函数只能用于转换由GetMessage或PeekMessage函数接收到的消息。

  • 消息WM_KEYDOWN和WM_KEYUP组合产生一个WM_CHAR或WM_DEADCHAR消息。
  • 消息WM_SYSKEYDOWN和WM_SYSKEYUP组合产生一个WM_SYSCHAR或 WM_SYSDEADCHAR 消息。

TtanslateMessage仅为那些由键盘驱动器映射为ASCII字符的键产生WM_CHAR消息。

如果应用程序为其它用途而处理虚拟键消息,不应调用TranslateMessage函数。例如,如果TranslateAccelerator函数返回一个非零值,则应用程序将不调用TranslateMessage函数。

Windows CE:Windows CE不支持扫描码或扩展键标志,因此,它不支持由TranslateMessage函数产生的WM_CHAR消息中的lKeyData参数(lParam)16-24的取值。

按我的理解:

  1. GetMessage取出消息1
  2. TranslateMessage转换消息1得到消息2,将消息2放入消息队列。
  3. 然后继续处理消息1.

 

DispatchMessage函数的作用是调度一个消息给窗口程序,通常调度从GetMessage取得的消息,消息被调度到的窗口程序即是窗口过程函数。

返回值是窗口程序返回的值,尽管返回值的含义依赖于被调度的消息,但返回值通常被忽略。

它的工作原理实际上是:

  1. 调用 DispatchMessage(&msg); 将消息结构msg回传给Windows
  2. 然后Windows将该消息发送给适当的窗口消息处理程序,让它进行处理。
  3. 处理完消息之后,WndProc(窗口过程函数) 传回到Windows,此时Windows还停留在DispatchMessage呼叫中。
  4. 在结束DispatchMessage呼叫的处理之后,Windows回到程序中,并且接着从下一个GetMessage呼叫开始消息循环。

 

完整的过程如下图:

  1. 操作系统将消息投递到消息队列中 或者 直接调用窗口过程函数(下文会讲解)。
  2. 程序调用GetMessage函数获得消息
  3. 程序可以进行一系列的处理,比如 TranslateMessage
  4. 调用 DispatchMessage 将消息传回到操作系统
  5. 操作系统调用窗口过程函数,待处理完后返回到程序。

以上是我目前理解的消息的全部过程,可能依旧有误,已经不敢说自己理解了消息了,万一又错了呢。

 

3. 队列消息与非队列消息

消息可以分为 队列消息 和 非队列消息,这个区分主要是根据消息有没有进入到线程的消息队列中。

队列化的消息由Windows放入程序(线程的)消息队列中,在程序的消息循环中,重新传回给Windows并由系统分配给窗口消息处理程序。

非队列化的消息在Windows呼叫窗口时直接送给窗口的消息处理程序 – 窗口过程函数。

也就是说,队列化的消息最初是被「发送」给消息队列,而非队列化的消息则直接「发送」给窗口消息处理程序,然后他们最终都到达了程序的消息处理中心,窗口过程函数。

 

队列消息基本上是用户输入的结果,以 比如 击键(如WM_KEYDOWN和WM_KEYUP消息)、击键产生的字符(WM_CHAR)、鼠标移动(WM_MOUSEMOVE)和鼠标按钮(WM_LBUTTONDOWN)的形式给出。

队列化消息还包含 时钟消息(WM_TIMER)、重绘消息(WM_PAINT)和 退出消息(WM_QUIT)。

 

非队列消息则是除队列消息外的其他消息,在许多情况下,非队列化消息来自呼叫特定的Windows函数。

举几个例子:

当WinMain调用 CreateWindow函数时,Windows将建立窗口并在处理中给窗口消息处理程序发送一个WM_CREATE消息。

当WinMain调用 ShowWindow函数时, Windows将给窗口消息处理程序发送WM_SIZE和WM_SHOWWINDOW消息。

当WinMain调用 UpdateWindow 时,     Windows将给窗口消息处理程序发送WM_PAINT消息。

 

键盘或鼠标操作时发出的消息信号,也能产生非队列消息。

例如,用键盘或鼠标选择了一个菜单项时,键盘或鼠标按下的消息就是队列消息,而说明菜单项已选中的 WM_COMMAND 消息则可能就是非队列化的。

 

4. SendMessage 和 PostMessage 的区别

之前面试经常问这个问题,但是总要想好久才想起来,这次经过 队列消息 和 非队列消息的学习,就很好理解了。

SendMessage就像发送非队列消息一样,直接调用窗口过程函数,然后得到返回值。

PostMessage就像发送队列消息,把消息放到消息队列中去,就直接退出了。

如下图:

 

 

5. GetMessage 和 PeekMessage 的区别

这个就比较简单了,两个函数主要有以下两个区别:

  1. GetMessage将等到有合适的消息时才返回,而PeekMessage只是看一下消息队列,有就拿出,没用就算了。
  2. GetMessage会将消息从队列中删除,而PeekMessage可以设置最后一个参数wRemoveMsg来决定是否将消息保留在队列中。

 

6. 消息队列与窗口、线程的关系

面试经常问的,引用CSDN博客里的回答:

线程是用来干活的, 一个进程内部可以有好几个干活的线程。

线程可以分为工作线程和UI线程:

带有界面的线程叫 UI 线程, 每个 UI 线程内会有一个消息队列,一个线程在第一调用 GDI、User 窗口函数时创建消息队列。

窗口属于创建窗口的线程,这些同一线程的窗口共享同一个消息循环,为了同步原因,最好只在创建窗口的线程内访问窗口。

【Windows程序设计】消息
Tagged on:     

发表评论

电子邮件地址不会被公开。 必填项已用*标注