PostMessage 不会触发消息处理程序
Posted
技术标签:
【中文标题】PostMessage 不会触发消息处理程序【英文标题】:PostMessage does not trigger Message Handler 【发布时间】:2014-01-06 08:25:56 【问题描述】:我有一个使用 PostMessage 触发函数调用的 VC++ 代码。尽管最终调用了该函数,但我看到该函数在 PostMessage 执行后并未立即被调用。在包含 PostMessage 命令的函数完成执行后,它甚至不会被调用。我注意到在调用所需函数之前调用了更多函数。
我想知道是什么决定了消息的回调何时被调用。
消息是 WM_USER 消息。
【问题讨论】:
PostMessage 是异步的,这种行为是根据定义的。如果要同步调用,请使用 SendMessage。一般在主应用线程被激活时调用message handler,处理所有在队列中等待的消息。 关于为什么没有立即调用该函数的任何线索?它只有在很长一段时间后才会被调用。它打乱了我的流程,因为我需要在其他一些东西执行之前调用该函数。 您发送的是哪条消息?您确定目标消息处理程序足够空闲以获取您的消息吗? 如果你从主线程调用 PostMessage,它不会启动,直到当前消息处理程序被执行。 请参阅About Messages and Message Queues - Message Routing,了解队列消息和非队列消息之间的区别。 【参考方案1】:简而言之 Windows 事件驱动模型(简化了许多重要细节):
有一个“消息泵”,它是一个循环,在每次迭代时,都会从队列中拉出一条消息,并通过调用与特定窗口关联的“窗口过程”来“调度”它,并将详细信息传递给它来自消息。
如果您使用的是框架,则可能会为您提供消息泵和窗口过程,并且提供的窗口过程会调用您的处理程序函数以响应它接收到的消息。
当消息被发布时,它被添加到队列中,在消息泵到达它之前不会发生任何其他事情。队列中可能有消息在它前面,和/或在执行返回到消息泵之前可能会运行一堆其他代码。
当消息发送时,该消息不会添加到队列中。相反,发送代码将直接调用窗口过程。 (警告:这过于简单化了。将消息发送到另一个线程时,事情会更加复杂。)
因此,如果在您发布的消息中期望的函数之前调用了其他函数,这里有一些可能性:
当您发布消息时,其他消息已在队列中。
其他消息正在发送而不是发布,因此它们会立即得到处理,只有在它们完成并且消息泵最终再次运行时才会处理您发布的消息。
消息泵决定在您之后发布的其他消息更重要,因此应首先处理。 (这实际上并不常见。某些类型的消息,如 WM_PAINT,优先级很低,仅在队列中没有任何内容时才生成,但我想不出消息优先于已经在队列中的消息的情况排队。)
您在初始化期间发布消息,甚至在消息泵启动之前。例如,如果您创建一个窗口,向其发布一条消息,然后启动消息泵,那么在您发布消息之前,队列中可能会有一系列与窗口创建相关的消息。
【讨论】:
"发送消息时,消息不加入队列,而是发送代码直接调用窗口过程。"仅当从拥有窗口的线程调用SendMessage
时才为真。否则,当从拥有窗口的线程调用GetMessage
(或类似的PeekMessage
,MsgWaitForMultipleObjectsEx
)时,它将调用窗口过程,然后返回下一个出列的已发布消息。
@BenVoigt:我认为我的大胆免责声明就足够了。有很多这样的细节使我写的不太真实。但是,我相信 OP 只需要一些基础知识就可以理解所描述的行为。
我认为省略细节是合适的。例如,您可以说“当从 UI 线程发送消息时......”并且从不描述跨线程行为。在我看来,简化到错误的程度比明确不涵盖某些情况更糟糕。【参考方案2】:
并非所有消息都是平等的。 PostMessage
不只是将它添加到队列数据结构中;操作系统会做很多操作和优先排序。不同的消息有不同的优先级,有些像WM_PAINT
这样的消息根本不能这样发送。
所以没有办法概括“PostMessage
何时会调用消息处理程序?”这取决于您发送的消息、目标消息队列的繁忙程度以及目标消息循环的行为方式。
我认为没有任何关于消息优先级的简明列表。
【讨论】:
以上是关于PostMessage 不会触发消息处理程序的主要内容,如果未能解决你的问题,请参考以下文章
PostMessage和SendMessage有什么区别?(有EnumChildWindowsProc的例子)
delphi 中 postmessage 和sendmessage用法