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

我理解那部分。我不明白的是事情之间的关系。具体来说:

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

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

有帮助吗?

解决方案

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

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

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

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

其他提示

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

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

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

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

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

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

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

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

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

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

杰弗里·里克特(Jeffrey Richter)的书有很多(全部?)血淋淋的细节。我的版本是旧的(高级Windows)。现在的版本好像叫 Windows 通过 C/C++.

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

“翻译”消息意味着什么?对 TranslateMessage() 的调用到底做了什么?

它监视虚拟按键消息,当它识别出按键按下/按键弹起组合时,它会添加字符消息。如果你不打电话 翻译消息, ,您将不会收到像 WM_CHAR 这样的字符消息。

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

一旦由 DispatchMessage() 分派,消息在到达我的 WndProc 之前会经过哪些地方(即操作系统用它做什么)?

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

为了解决最后一个子问题,分派的消息在通过所有挂钩(WH_CALLWNDPROC)传输后将转到您的 WindowProc

对此并不绝对积极,但我最好的猜测是:

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

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

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

希望有帮助。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top