如何从不同的线程执行SendMessage?

时间:2010-05-31 10:16:06

标签: windows winapi

当我们发送消息时,“如果指定的窗口是由调用线程创建的,则窗口过程将立即作为子例程调用”。 但是“如果指定的窗口是由不同的线程创建的,则系统切换到该线程并调用相应的窗口过程。线程之间发送的消息仅在接收线程执行消息检索代码时处理。” (摘自SendMessage的MSDN文档。)

现在,我不理解如何(或更恰当地,时)调用目标Windows过程。当然,目标线程不会被抢占(程序计数器不会被更改)。我假设调用会在某些等待函数(如GetMessagePeekMessage)中发生,这是真的吗?那个过程在某个地方详细记录了吗?


更新:其背后的基本原理由QS_SENDMESSAGEGetQueueStatus()的{​​{1}}标志解释:

MsgWaitForMultipleObjects()

这与MSDN文档中的其他注释一起意味着另一个线程发送的消息实际上已发布到队列中。然后,只要调用QS_SENDMESSAGE A message sent by another thread or application is in the queue. GetMessage,就会在任何其他发布消息之前通过直接发送到窗口过程来处理它。

3 个答案:

答案 0 :(得分:5)

我在这看到一些困惑。

根据MSDN文档,当您触摸当前线程的消息队列以进行消息处理时(例如,如果您致电PeekMessageGetMessage),全部处理来自其他线程的待发送(即非排队)消息 - 传递给WndProc - 然后检查消息队列,所以:

  • 已发送消息从不通过DispatchMessage并尽快处理:
    • 在当前主题中,它们只是传递给WndProc
    • 在另一个帖子中,在任何发布消息处理之前处理它们
  • 为了能够处理已发送的消息,目标线程仍需要消息泵
  • PostThreadMessage完成它所声明的内容 - 发布线程队列中的消息 - 此类消息不会定向到任何窗口,必须经过精心处理
  • DispatchMessage处理的消息是由PostMessage或某些系统工具(定时器,事件,用户输入等)创建的消息
  • 为避免死锁,请在不同主题之间使用SendNotifyMessageSendMessageTimeoutSendMessageCallback代替普通SendMessage

如需进一步参考,请研究MSDN PeekMessage条目的备注部分。

答案 1 :(得分:1)

简答:当目标线程调用GetMessage(或PeekMessage)后跟DispatchMessage时,接收并处理来自另一个线程的SendMessage。

我不确定收到的SendMessage是否会抢占队列中的其他消息。无论哪种方式,从一个线程到另一个线程的SendMessage就像是说:“将此消息发布到另一个线程的消息队列。当该线程完成处理后返回”。

现在回答你没有要求:

通常,当我编写主UI线程和工作线程之间的交互时,我会尽量避免使用SendMessage。如果你不小心,你可能会遇到两个线程彼此陷入僵局的情况。 (想想主线程调用WaitForSingleObject来等待工作线程完成的情况,但工作线程在SendMessage上被阻止回UI线程。)

答案 2 :(得分:1)

每个窗口都与一个线程相关联。您可以使用GetWindowThreadProcessId检索每个窗口的线程。如果您从PostThreadMessage的其他线程向Windows发送消息,则消息将被放置在线程的消息队列中。线程必须有一个get-message循环(例如GetMessage)来获取消息并将其分派到窗口的窗口过程。

您调用SendMessage而不是PostThreadMessage直接调用Windows过程而不将其放在消息队列中。一些非排队消息也会立即发送到目标窗口过程,绕过系统消息队列和线程消息队列。 (见http://msdn.microsoft.com/en-us/library/ms644927(VS.85).aspx#nonqueued_messages)。如果您想从另一个窗口(控件)提供一些信息,例如在处理另一个消息期间从另一个控件读取文本,则使用SendMessage而不是PostThreadMessage的主要原因。你应该只在真正需要的时候这样做。因此,如果您使用SendMessage从另一个线程向Windows发送消息,则必须阻止当前线程一段时间。

如果可能的话,最好使用PostThreadMessageSendMessageCallback代替SendMessage