Win32 中消息队列如何工作?

发布于 01-04 04:36 字数 207 浏览 2 评论 0原文

我读了一些关于 Win32 以及消息循环如何工作的内容,但有一些东西我仍然不清楚:消息队列中到底存储了什么?对应于消息(WM_COMMANDWM_CREATE 等)的整数值或指向包含消息整数值和其他内容的 MSG 结构的指针像 wParamlParam 之类的东西?

I read some stuff on Win32 and how the message loop works, and there's something that is still unclear to me: What exactly is stored in the message queue? The integer value that corresponds to a message (WM_COMMAND, WM_CREATE, etc) or a pointer to a MSG structure containing the message integer value and other stuff like wParam, lParam, etc?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

七度光2025-01-11 04:36:46

为了狭隘地回答你的问题,队列中的每条消息至少存储

  • 一个消息定向的窗口句柄、
  • 消息代码、wParam和lParam,正如你已经正确指出的那样,
  • 消息发布的时间, 对于 UI 消息,您可以使用 GetMessageTime() 检索
  • 消息发布时的光标位置(请参阅 GetMessagePos())。

请注意,并非所有消息实际上都存储在队列中。使用 SendMessage() 从拥有窗口的线程发送到窗口的消息永远不会被存储;相反,直接调用接收者窗口的消息函数。从其他线程发送的消息将被存储直到被处理,并且发送线程会阻塞,直到通过退出窗口函数或显式调用 ReplyMessage() 来回复消息。 API 函数 InSendMessage() 有助于确定 Windows 函数是否正在处理从另一个线程发送的消息。

您或系统发布的消息都存储在队列中,但有一些例外。

  • WM_TIMER 消息实际上从未存储;相反,如果队列中没有其他消息并且计时器已到期,则 GetMessage() 会构造一条计时器消息。这意味着,首先,计时器消息具有最低的出队优先级,其次,即使一段时间内没有调用 GetMessage(),来自短周期计时器的多个消息也永远不会溢出队列。因此,即使自处理来自该计时器的最后一个 WM_TIMER 消息以来该计时器已经经过多次,也会为给定计时器发送单个 WM_TIMER 消息。

  • 同样,WM_QUIT 也不会被存储,而只是被标记。 GetMessage() 假装在队列耗尽后检索到了 WM_QUIT,这是它检索到的最后一条消息。

  • 另一个例子是 WM_PAINT 消息(向 @cody-gray 致敬以提醒您这一点)。当窗口的任何部分被标记为“脏”并且需要重新绘制时,也会模拟此消息。这也是一条低优先级消息,以便在队列变空时立即重新绘制窗口中的多个无效区域,以降低 GUI 的响应能力并减少闪烁。您可以通过调用 UpdateWindow() 强制立即重新绘制。该函数的作用类似于 SendMessage(),从某种意义上说,它在窗口的暴露部分被重绘之前不会返回。如果该窗口的无效区域为空,则此函数不会向该窗口发送 WM_PAINT,这是一个明显的优化。

可能还有其他例外和内部优化。

使用 PostMessage() 发布的消息最终会出现在拥有消息发布到的窗口的线程的队列中。

消息在内部以什么形式存储,我们不知道,也不关心。 Windows API 完全抽象了这一点。 MSG 结构填充在您传递给 GetMessage()PeekMessage() 的内存中。除了 Windows SDK 指南中记录的内容之外,您无需了解或担心内部实现的细节。


1 我不知道 WM_PAINT 和 WM_TIMER 相对于彼此的优先级究竟如何。我认为 WM_PAINT 的优先级较低,但我可能是错的。

To answer your question narrowly, each message in the queue stores, at the least,

  • a window handle to which the message is directed,
  • the message code, wParam and lParam, as you already correctly noted,
  • the time when the message was posted, that you retrieve with GetMessageTime(),
  • for UI messages, the position of the cursor when the message was posted (see GetMessagePos()).

Note that not all messages are actually stored in the queue. Messages that are sent with SendMessage() to a window from the thread that owns the window are never stored; instead, a receiver window's message function is called directly. Messages sent from other threads are stored until processed, and the sending thread blocks until the message is replied to, either by exiting the window function or explicitly with a call to ReplyMessage(). The API function InSendMessage() helps figure out whether the windows function is processing a message sent from another thread.

Messages that you or the system post are stored in the queue, with some exceptions.

  • WM_TIMER messages are never actually stored; instead, GetMessage() constructs a timer message if there are no other messages in the queue and a timer has matured. This means that, first, the timer messages have the lowest dequeuing priority, and, second, that multiple messages from a short period timer would never overflow queue, even if GetMessage() is not called for a while. As a result, a single WM_TIMER message is sent for the given timer, even if the timer has elapsed multiple times since the last WM_TIMER message from that timer has been processed.

  • Similarly, WM_QUIT is also not stored, and rather only flagged. GetMessage() pretends to have retrieved the WM_QUIT after the queue has been exhausted, and this is the last message it retrieves.

  • Another example is the WM_PAINT message (hat tip to @cody-gray for reminding about this). This message is also simulated when any part of the window¹ is marked as "dirty" and needs repainting. This is also a low-priority message, made so that multiple invalidated regions in a window are repainted all at once when the queue becomes empty, to reduce responsiveness of the GUI and reduce flicker. You can force an immediate repaint by calling UpdateWindow(). This function acts like SendMessage(), in the sense that it does not return until the exposed part of the window is redrawn. This function does not send a WM_PAINT to the window if the invalid region of that window is empty, as an obvious optimization.

Probably, there are other exceptions and internal optimizations.

Messages posted with PostMessage() end up in the queue of a thread that owns the window to which the message is posted.

In what form the messages are stored internally, we do not know, and we do not care. Windows API abstracts that completely. The MSG structure is filled in memory you pass to GetMessage() or PeekMessage(). You do not need to know or to worry about the details of internal implementation beyond those documented in Windows SDK guides.


¹ I do not know how exactly WM_PAINT and WM_TIMER are prioritized relative to each other. I assume WM_PAINT has a lower priority, but I may be wrong.

荒岛晴空2025-01-11 04:36:46

Windows 中的消息队列是一个抽象。将其视为队列非常有帮助,但它的实际实现要详细得多。 Windows 中有四种不同的消息源:

  • 由 SendMessage() 传递的消息。 Windows 直接调用窗口过程,消息不是由 Peek/GetMessage() 返回,但需要调用该函数才能调度。到目前为止,大多数消息都是通过这种方式传递的。 WM_COMMAND 就是这样,它是由翻译按键事件的代码直接发送的,如 TranslateAccelerator()。没有类似队列的行为。

  • 从窗口状态合成的消息。最好的例子是 WM_PAINT,当设置“窗口有脏矩形”状态标志时传递。和WM_TIMER,当“定时器已过期”状态标志被设置时传递。这些是“低优先级”消息,仅在消息队列为空时传递。它们由 GetMessage() 传递,但不会存在于队列中。

  • 键盘和鼠标的输入事件消息。这些是真正具有类似队列行为的消息。对于键盘来说,这可以确保提前输入功能,当程序未准备好接受击键时,不会丢失击键。一堆状态与之关联,例如键盘的整个状态被复制。对于鼠标来说大致相同,只是状态较少。 WM_MOUSEMOVE 消息是一个有趣的极端情况,队列存储光标遍历的每个像素。位置变化会累积到一条消息中,并在必要时存储或传递。

  • 通过显式 PostMessage() 调用将消息存储在队列中。这完全取决于程序代码,显然它只需要存储调用的参数加上调用的时间,以便可以在 GetMessage() 时间准确地重放。

The message queue in Windows is an abstraction. Very helpful to think of it as a queue but the actual implementation of it is far more detailed. There are four distinct sources of messages in Windows:

  • Messages that are delivered by SendMessage(). Windows directly calls the window procedure, the message isn't returned by Peek/GetMessage() but does require a call to that function to get dispatched. By far the most messages are delivered this way. WM_COMMAND is like that, it is directly sent by the code that translates a key-down event, like TranslateAccelerator(). No queue-like behavior.

  • Messages that are synthesized from the window state. Best examples are WM_PAINT, delivered when the "window has a dirty rectangle" state flag is set. And WM_TIMER, delivered when the "timer has expired" state flag is set. These are 'low priority' messages, only delivered when the message queue is empty. They are delivered by GetMessage() but do not otherwise live on the queue.

  • Input event messages for the keyboard and mouse. These are the messages that truly have queue-like behavior. For the keyboard this ensures that type-ahead works, no keystroke gets lost when the program isn't ready to accept a keystroke. A bunch of state is associated with it, the entire state of the keyboard gets copied for example. Roughly the same for the mouse except that there's less state. The WM_MOUSEMOVE message is an interesting corner case, the queue does not store every single pixel traversed by the cursor. Position changes are accumulated into a single message, stored or delivered when necessary.

  • Messages stored in the queue by an explicit PostMessage() call. That's entirely up to the program code, clearly it only needs to store the arguments of the call plus the time the call was made so it can accurately be replayed back at GetMessage() time.

九歌凝2025-01-11 04:36:46

MSDN 有一篇很好的文章这里< /a> 解释消息和消息队列上的所有内容。

但要回答您的问题,每个窗口都存在队列,它临时存储消息及其关联参数,无论它的 MSG 队列是否是实现定义的,但最有可能的是(或类似的东西)。还应该注意的是,并非所有消息都会进入队列,有些消息需要立即处理。

MSDN has a nice article here explaining everything on messages and message queues.

But to answer your question, the Queue exists for each window, it temporarily stores the messages and their associate parameters, whether of not its a queue of MSG's is implementation defined, but it most likely is (or something similar). It should also be noted that not all messages will go to the queue, some require immediate processing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文