Win32 事件驱动编程在幕后是如何实现的?

发布于 2024-07-17 12:42:17 字数 431 浏览 6 评论 0原文

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

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

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

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

In a Win32 C++ application, we start a message loop that fetches messages from a queue, translates them and then dispatches them. Eventually, each message reaches our WndProc where the associated event can be handled.

I understand that part. What I don't understand is the in between goings-on. Specifically:

  1. Different kinds of OS interrupt handlers must be placing messages in the said 'message queue', but where within the process address space does this queue reside? How is it exposed to the interrupt handler code?
  2. What does it mean to 'translate' the message? What does the call to TranslateMessage() really do?
  3. Once dispatched by DispatchMessage(), what all places does the message swing by before reaching my WndProc (i.e. what does the OS do with it)?

If anyone knows the answers to the above, kindly satisfy my curiosity. Thanks.

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

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

发布评论

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

评论(5

只有一腔孤勇 2024-07-24 12:42:18

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

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

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

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

The OS maintains a message queue, where it puts the events (e.g., from interrupts or other sources). It then sends the messages from that queue to all windows, depending on the message (e.g., it won't send key messages to a window that doesn't have focus).

Applications can have their own queue to process messages. Those queues are created on request (only if needed).

Translating a message is used to create messages that are not 'real' events. For example, the WM_CONTEXTMENU message is 'translated' from either a mouse right-click, or the context menu key, or shift-F10. The WM_CHAR is translated from WM_KEYDOWN messages. And of course many other messages are 'translated' that way.

A message is posted to every window that should receive it. The OS decides depending on the type of message whether a window should receive that message or not. Most messages are waited for by the system, i.e., the message won't get posted to another window until it was processed by the window. This has a great impact for broadcast messages: if one window doesn't return when handling that message, the queue is blocked and other windows won't receive the message anymore.

笑脸一如从前 2024-07-24 12:42:18

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

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

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

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

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

It depends on how your message is sent and how it's handled.

When you call SendMessage, if the target window is owned by the current thread, the call bypasses the message queue for the window and the window manager directly calls the windowproc on the target window. If the target window is owned by another thread, the window manager effectively calls PostMessage and pumps window messages until the target window returns from the window proc.

When you call PostMessage, the window manager marshals the message parameters and inserts the corresponding object onto the message queue for the target window. When it next calls GetMessage the message is removed from the message queue.

The window manager also registers for raw input events from the input devices (keyboard and/or mouse) and it generates messages for those input events. It then inserts those messages in the queue as appropriate (the processing of input events is complicated because it depends on what messages are already in the message queue for the window).

As Stefan indicated, TranslateMessage just translates accelerator keys - for instance it converts key sequences to WM_COMMAND messages.

幸福%小乖 2024-07-24 12:42:18

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

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

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

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

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

杰弗里·里克特(Jeffrey Richter)的书有很多(全部?)血淋淋的细节。 我的版本是旧的(高级 Windows)。 当前版本似乎称为Windows via C/C++

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

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

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

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

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

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

Different kinds of OS interrupt handlers must be placing messages in the said 'message queue', but where within the process address space does this queue reside? How is it exposed to the interrupt handler code?

Windows are associated with threads. Each thread with a window has a thread queue in the process's address space. The OS has an internal queue in its own address space for the hardware-generated events. Using details of the event and other state information (e.g., which window has the focus), the OS translates the hardware events into messages that are then placed in the appropriate thread queue.

Messages that are posted are placed directly in the thread queue for the target window.

Messages that are sent are usually processed directly (bypassing the queue).

The details get hairy. For example, the thread queues are more than lists of messages--they also maintain some state information. Some messages (like WM_PAINT) aren't really queued, but synthesized from the additional state information when you query the queue and it's empty. Messages sent to windows owned by other threads are actually posted to the receiver's queue rather than being processed directly, but the system makes it appear like a regular blocking send from the caller's point of view. Hilarity ensues if this can cause deadlock (because of circular sends back to the original thread).

The Jeffrey Richter books have a lot (all?) of the gory details. My edition is old (Advanced Windows). The current edition seems to be called Windows via C/C++.

The OS does a LOT of work to make the message stream appear rational (and relatively simple) to the caller.

What does it mean to 'translate' the message? What does the call to TranslateMessage() really do?

It watches for virtual key messages and, when it recognizes a key-down/key-up combination, it adds character messages. If you don't call TranslateMessage, you won't receive character messages like WM_CHAR.

I suspect it sends the character message directly before returning (as opposed to posting them). I've never checked, but I seem to recall that the WM_CHAR messages arrive just before the WM_KEYUP.

Once dispatched by DispatchMessage(), what all places does the message swing by before reaching my WndProc (i.e. what does the OS do with it)?

DispatchMessage passes the message to the WndProc for the target window. Along the way, it some hooks may get a chance to see the message (and possibly interfere with it).

山色无中 2024-07-24 12:42:18

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

To address the last subquestion, a dispatched message will go to your WindowProc after it's been piped through all hooks (WH_CALLWNDPROC)

青芜 2024-07-24 12:42:18

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

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

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

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

希望有帮助。

Not absolutely positive about this but my best guess says:

  1. The queue is a system object that you access with Win32 API calls. It is not in your process address space at all. So the interrupt handlers can access it (probably through the HAL (Hardware Abstraction Layer) of the kernel).

  2. In Win16, that call took the various subparts of a bigger message and mashed them into a whole. So TranslateMessage would add the WM_KEYPRESS when it found the corresponding WM_KEYDOWN WM_KEYUP sequence. It would also turn various button click messages into doubleclick messages based on internal setting and the timestamps of the messages. Whether it still does this in Win32, I don't know.

  3. DispatchMessage is probably where Window message hooks get processed. So if there's a hook on your window, it is either called here or when the GetMessage is called. I'm not sure. Other than that, DispatchMessage just looks up the WndProc address associated with the window and calls it. There's not much else for it to do.

Hope that helps.

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