最近我尝试将一个窗口的消息循环放在它自己的线程中,我想知道为什么它从来没有收到任何消息,但我了解到 Windows 将消息发布到创建窗口的线程。如何在一个线程中创建一个窗口并让另一个线程接收该窗口的消息?我见过 PostThreadMessage 函数,但我相信它还需要创建窗口的线程来监听消息,这正是我试图避免的事情,所以这个函数不是我需要的。
这似乎是一个常见问题,我花了很多时间在谷歌上搜索答案,但我找不到答案。
最近我尝试将一个窗口的消息循环放在它自己的线程中,我想知道为什么它从来没有收到任何消息,但我了解到 Windows 将消息发布到创建窗口的线程。如何在一个线程中创建一个窗口并让另一个线程接收该窗口的消息?我见过 PostThreadMessage 函数,但我相信它还需要创建窗口的线程来监听消息,这正是我试图避免的事情,所以这个函数不是我需要的。
这似乎是一个常见问题,我花了很多时间在谷歌上搜索答案,但我找不到答案。
如何在一个线程中创建一个窗口并让另一个线程接收该窗口的消息?
简单的答案......你没有。在要处理其消息的线程上创建窗口。如果这是不可能的,那么您需要重新考虑您的方法。
Windows 消息泵实际上只是一个循环,它使用并调用您的 Windows WndProc 函数while
从队列中挑选消息。PeekMessage()
还有更多的东西,但这是基本操作。
因此,while
循环运行的任何线程都是您的窗口可以“运行”的唯一线程。这就是我所见过的每个 Windows 应用程序的构建方式。
但是,我过去认为,只要付出大量努力,就应该可以在多个线程中构建一个带有窗口的 Windows 应用程序。我没有任何代码可以向您展示,因为我已经很久没有考虑过这个问题了,但是我考虑了两种方法:
在主线程中保留一个消息泵。但是修改消息泵代码,以便在它使用基于特定运行的线程while
将消息分派给工作线程。为此需要查找映射,这可能在计算上很昂贵。QueueUserAPC
HWND
在工作线程中创建一个全新的消息泵。您必须编写所有while
代码,但这很简单,Petzold 的经典著作 将为您提供执行此操作所需的所有工具。
请注意,从架构的角度来看,这种方法对于我能想到的任何应用程序都没有意义。如果您合理地构建您的应用程序,您根本不需要窗口在多个线程中运行。窗口处理发生在一个线程中,操作发生在另一个(或多个)线程中。然而,我认为这是一个有趣的研究领域,这也是我走上这条道路的原因。长话短说,我几乎可以肯定你不需要这样做,也不应该这样做——但这可能就是你会这样做的方式。
这是不可能的。每个窗口都属于创建它的线程,并且该所有权不能转移。
这不是将您的消息泵放在另一个线程中的问题。每个线程都有自己的消息队列。当您向窗口发送或发布消息时,操作系统会检查哪个线程拥有该窗口并将消息定向到该线程的消息队列。线程不能读取除自己的消息队列外的任何消息队列,因此您不能让线程处理另一个线程窗口的消息。
您可以将消息重新发送到另一个线程,就像约翰的回答中的第一个想法一样,但是作为一个通用的消息处理程序,这将变得比它的价值更复杂。许多消息旨在修改窗口的状态,但除了窗口自己的线程之外,您不能修改状态。有些消息是为了获得有意义的返回值而发送的,但是在消息被处理之前你不知道要返回什么,所以你必须阻塞,等待工作线程处理消息。
您最好识别真正可以卸载到工作线程的一小组消息并专门处理它们。一旦你这样做了,你就不会真正有一个窗口,它的消息在不同的线程中处理。您将只有一个普通的工作线程,并且推理起来不会那么混乱。
如果有消息发送到您的窗口需要大量时间来处理,但发件人不需要知道结果,或者您在完成处理之前就知道结果,那么您可以通过调用提供早期响应ReplyMessage
. 这让发送线程继续运行,而您的窗口线程执行额外的工作。
您可以查看AttachThreadInput是否适合您 - 它可以让您处理来自其他线程的消息。
这确实不是一个常见的问题,因为您几乎总是在创建它的线程中处理窗口消息。见 Goz 的回答;我同意。
请注意,将消息处理放在另一个线程中将一无所获。不要将 GUI 任务分解为多个线程,将处理/后台任务分解为线程。
看到这个:
msdn: http: //msdn.microsoft.com/en-us/library/ms644946 (v=vs.85).aspx
消息发布到的线程必须已经创建了消息队列,否则对 PostThreadMessage 的调用将失败。使用以下方法来处理这种情况。
1.创建一个事件对象,然后创建线程。
2.使用WaitForSingleObject函数等待事件设置为signaled状态,然后再调用PostThreadMessage。
3.在将要发布消息的线程中,调用此处所示的 PeekMessage 以强制系统创建消息队列。
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE)
4.设置事件,表示线程已准备好接收发布的消息。