如何在引擎盖下实现Win32事件驱动编程?

时间:2009-04-19 13:35:24

标签: windows winapi message-queue event-driven

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

我理解那一部分。我不明白的是介于两者之间。具体做法是:

  1. 不同类型的OS中断处理程序必须将消息放入所述“消息队列”中,但该队列驻留在进程地址空间中的哪个位置?它是如何暴露给中断处理程序代码的?
  2. “翻译”消息意味着什么?对TranslateMessage()的呼吁到底是做什么的?
  3. 一旦被DispatchMessage()发送,在到达我的WndProc之前,消息会在什么地方摆动(即操作系统用它做什么)?
  4. 如果有人知道上述答案,请满足我的好奇心。感谢。

5 个答案:

答案 0 :(得分:6)

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

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

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

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

答案 1 :(得分:6)

这取决于您的邮件的发送方式以及邮件的处理方式。

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

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

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

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

答案 2 :(得分:4)

  

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

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

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

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

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

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

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

  

“翻译”消息意味着什么?对TranslateMessage()的调用真的如何?

它监视虚拟密钥消息,当它识别出按键/按键组合时,它会添加字符消息。如果您不拨打TranslateMessage,则不会收到WM_CHAR等字符消息。

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

  

一旦被DispatchMessage()调度,消息在到达我的WndProc之前会在什么位置摆动(即操作系统用它做什么)?

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

答案 3 :(得分:1)

对此并不是绝对正面,但我最好的猜测是:

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

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

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

  4. 希望有所帮助。

答案 4 :(得分:1)

要解决最后一个子问题,在通过所有挂钩管道(WH_CALLWNDPROC)之后,调度的消息将转到您的WindowProc