11

在 Win32 C++ 应用程序中,我们启动一个消息循环,从队列中获取消息,翻译它们,然后分派它们。最终,每条消息都会到达我们的 WndProc,在那里可以处理相关的事件。

我理解那部分。我不明白的是中间发生的事情。具体来说:

  1. 不同类型的 OS 中断处理程序必须将消息放入所述“消息队列”中,但是该队列驻留在进程地址空间的哪个位置?它是如何暴露给中断处理程序代码的?
  2. “翻译”信息是什么意思?调用的TranslateMessage()真正作用是什么?
  3. 一旦被发送DispatchMessage(),消息在到达我的 WndProc 之前会经过哪些地方(即操作系统如何处理它)?

如果有人知道以上问题的答案,请满足我的好奇心。谢谢。

4

5 回答 5

6

这取决于您的消息的发送方式和处理方式。

调用 SendMessage 时,如果目标窗口属于当前线程,则调用绕过窗口的消息队列,窗口管理器直接调用目标窗口上的 windowproc。如果目标窗口由另一个线程拥有,则窗口管理器有效地调用 PostMessage 并泵送窗口消息,直到目标窗口从窗口进程返回。

当您调用 PostMessage 时,窗口管理器会编组消息参数并将相应的对象插入到目标窗口的消息队列中。当它下一次调用 GetMessage 时,该消息将从消息队列中删除。

窗口管理器还注册来自输入设备(键盘和/或鼠标)的原始输入事件,并为这些输入事件生成消息。然后它将这些消息适当地插入到队列中(输入事件的处理很复杂,因为它取决于窗口的消息队列中已经有哪些消息)。

正如 Stefan 所指出的,TranslateMessage 只是翻译加速键 - 例如它将键序列转换为 WM_COMMAND 消息。

于 2009-04-19T22:16:07.610 回答
6

操作系统维护一个消息队列,它将事件放置在其中(例如,来自中断或其他来源)。然后它将消息从该队列发送到所有窗口,具体取决于消息(例如,它不会将关键消息发送到没有焦点的窗口)。

应用程序可以有自己的队列来处理消息。这些队列是根据请求创建的(仅在需要时)。

翻译消息用于创建不是“真实”事件的消息。例如,WM_CONTEXTMENU 消息是通过鼠标右键单击、上下文菜单键或 shift-F10 来“翻译”的。WM_CHAR 是从 WM_KEYDOWN 消息转换而来的。当然,许多其他消息都是以这种方式“翻译”的。

一条消息被发布到应该接收它的每个窗口。操作系统根据消息的类型决定窗口是否应该接收该消息。大多数消息由系统等待,即消息在被窗口处理之前不会被发送到另一个窗口。这对广播消息有很大的影响:如果一个窗口在处理该消息时没有返回,则队列被阻塞,其他窗口将不再接收该消息。

于 2009-04-19T14:19:59.473 回答
4

不同类型的 OS 中断处理程序必须将消息放入所述“消息队列”中,但是该队列驻留在进程地址空间的哪个位置?它是如何暴露给中断处理程序代码的?

窗口与线程相关联。每个具有窗口的线程在进程的地址空间中都有一个线程队列。操作系统在其自己的地址空间中有一个用于硬件生成事件的内部队列。使用事件的详细信息和其他状态信息(例如,哪个窗口具有焦点),操作系统将硬件事件转换为消息,然后将这些消息放置在适当的线程队列中。

发布的消息直接放置在目标窗口的线程队列中。

发送的消息通常直接处理(绕过队列)。

细节变得毛茸茸的。例如,线程队列不仅仅是消息列表——它们还维护一些状态信息。一些消息(如 WM_PAINT)并没有真正排队,而是在您查询队列并且它是空的时从附加状态信息合成。发送到其他线程拥有的窗口的消息实际上是发布到接收者的队列而不是直接处理,但是从调用者的角度来看,系统使它看起来像一个常规的阻塞发送。如果这会导致死锁(因为循环发送回原始线程),就会引起欢闹。

Jeffrey Richter 的书有很多(全部?)血腥细节。我的版本是旧的(高级 Windows)。当前版本似乎通过 C/C++ 称为 Windows

操作系统做了很多工作来使消息流在调用者看来是合理的(并且相对简单)。

“翻译”信息是什么意思?调用 TranslateMessage() 的真正作用是什么?

它监视虚拟按键消息,当它识别出按键/按键组合时,它会添加字符消息。如果您不调用TranslateMessage,您将不会收到 WM_CHAR 之类的字符消息。

我怀疑它在返回之前直接发送字符消息(而不是发布它们)。我从未检查过,但我似乎记得 WM_CHAR 消息在 WM_KEYUP 之前到达。

一旦被 DispatchMessage() 发送,消息在到达我的 WndProc 之前会经过哪些地方(即操作系统如何处理它)?

DispatchMessage 将消息传递给目标窗口的 WndProc。一路上,它的一些钩子可能有机会看到消息(并可能干扰它)。

于 2009-05-13T22:56:26.217 回答
1

为了解决最后一个子问题,在通过所有钩子 (WH_CALLWNDPROC) 传递消息后,发送的消息将发送到您的 WindowProc

于 2009-04-20T11:31:48.603 回答
1

对此并不绝对肯定,但我最好的猜测是:

  1. 队列是您通过 Win32 API 调用访问的系统对象。它根本不在您的进程地址空间中。因此中断处理程序可以访问它(可能通过内核的 HAL(硬件抽象层))。

  2. 在 Win16 中,该调用将更大消息的各个子部分合并为一个整体。所以 TranslateMessage 会在找到对应的 WM_KEYDOWN WM_KEYUP 序列时添加 WM_KEYPRESS。它还会根据内部设置和消息的时间戳将各种按钮单击消息转换为双击消息。在Win32中是否仍然这样做,我不知道。

  3. DispatchMessage 可能是处理 Window 消息挂钩的地方。因此,如果您的窗口上有一个钩子,它要么在此处调用,要么在调用 GetMessage 时调用。我不确定。除此之外,DispatchMessage 只是查找与窗口关联的 WndProc 地址并调用它。它没有太多其他事情要做。

希望有帮助。

于 2009-04-19T14:12:07.760 回答