当子窗口冻结时,即使它来自另一个进程,父窗口也会冻结。

26

免责声明: 我并不熟悉Win32 API,尤其是窗口如何工作。

我想将某个进程的窗口作为另一个进程的子窗口。这两个进程也有父子关系,但我认为这并不重要。到目前为止,一切都像魔法一样顺利 - 直到我冻结了子窗口的主线程。

想象一个container.exe“托管”notepad.exe和someApplication.exe

当我暂停someApplication.exe的主线程几秒钟时,它的窗口在那段时间内会被冻结。这是完全可以理解的。但是,container.exe的窗口也会同时被挂起相同的时间。其他托管进程(如notepad.exe)的子窗口仍将正常工作。

我正在使用SetParent命令将普通非子窗口设置为我的container.exe的子窗口:

SetParent(
    childProcess.HWND,
    myOwnHWND
);

之后,我使用setWindowPos

SetWindowPos(
    childProcess.HWND,
    HWND_TOP,
    someXPos,
    someYPos,
    0,
    0,
    SWP_FRAMECHANGED or SWP_NOSIZE or SWP_SHOWWINDOW
)

MSDN关于SetParent的文章所建议的,我也清除了WS_POPUP样式属性并添加了一个WS_CHILD属性。由于这也没有帮助,我还通过使用SetWindowLongPtr命令添加了一个WS_EX_NOACTIVATE扩展样式属性。最后,我尝试向两个窗口发送WM_UPDATEUISTATEWM_CHANGEUISTATE消息,但这也没有改变什么。

令我困惑的是,父进程的窗口在没有触摸它之前仍然正常绘制,然后完全冻结,直到子窗口解冻。我怀疑这是一种称为“输入队列”的东西。MSDN关于WM_ACTIVATE消息的文章中有述:

发送给正在激活的窗口和正在停用的窗口。如果窗口使用相同的输入队列,则将消息同步地发送,首先发送到正在停用的顶级窗口的窗口过程,然后发送到正在激活的顶级窗口的窗口过程。如果窗口使用不同的输入队列,则将消息异步地发送,因此窗口立即激活。

由于这个原因,我对WS_EX_NOACTIVATE扩展样式属性寄予厚望。

总结一下:实际上有可能托管另一个进程的窗口而在子窗口冻结时不冻结自己的窗口吗?


10
这个问题非常详尽。 - Jerry Dodge
1
你为什么需要这样做?我确信有更好、更简单的方法来达到你实际的目的。不要陷入 XY 问题的误区。 - Igor Skochinsky
2
@GSerg:正是我想要发布的链接。通过以父/子方式绑定两个或多个进程的Windows,您可以绑定它们的输入队列。如果您阻止其中任何一个输入队列,则所有Windows都会被阻止。 - Jeroen Wiert Pluimers
请查看http://stackoverflow.com/questions/40525884/hosting-wpf-plugin-cross-process-issue/40586377#40586377。 - obi111
1个回答

18

您不能期望阻止任何进程的GUI线程。在您的方案中,事情变得更加复杂,因为有两个GUI线程,每个进程一个。

然而,通过在这些进程的窗口之间建立父/子关系,您还引入了一个要求两个GUI线程及时服务的要求。

处于父/子关系的窗口将相互发送消息。如果这些消息是同步的(即发送而不是发布),那么阻塞一个GUI线程将导致另一个被阻塞。

GUI编程的黄金法则仍然有效:不要阻塞GUI线程。如果您有长时间运行的任务,则将其移至后台线程。

更新

好吧,如此处所解释的那样,当您将来自不同线程的窗口相关联时,会将它们的消息队列连接到彼此。因此,如果阻塞一个线程,就会阻塞所有已连接的线程。

所以,不要阻塞GUI线程。


1
谢谢。当然,GUI线程不应该被阻塞。但是我正在测试最坏的情况,比如托管应用程序完全冻结。在这种情况下,主机应用程序绝不能冻结。此外,我仍然想知道父/子进程关系在这里如何发挥作用。如果子进程窗口不是父进程窗口的子级,则后者当然不会冻结。因此,它们实际上无法“发送”消息。 - Günther the Beautiful
如果子进程窗口不是父进程窗口的子级,则后者当然不会冻结。您对SetParent的调用使一个窗口成为另一个窗口的子级。"他们真的不能发送消息吗?" 为什么不呢?整个Windows GUI都是基于消息的。这绝对是窗口所做的。如果阻塞GUI线程,那么请期望您的GUI失败。 - David Heffernan
2
所以关键点是现在两个窗口都使用相同的 _输入队列_。核心问题在于将同步的 WM_ACTIVATE 消息发送到冻结的窗口。这里有另一个新闻组帖子,涵盖了完全相同的主题:[链接](http://narkive.com/YmRDFrDR)。由于我不指望能够使用像 AttachThreadInput 命令之类的东西分离子窗口的 _输入队列_,所以我接受这个答案,并过上没有父/子窗口关系的幸福生活。非常感谢! - Günther the Beautiful
即使没有队列依赖,发送消息也会使您感到困扰。从另一个进程托管窗口是危险的行为。 - David Heffernan
当您将来自不同线程的窗口相关联时,您会将它们的消息队列彼此附加。正如您发布的链接所解释的那样,这确实是输入队列。此外,同步发送消息并不是什么大问题。一个线程可以在等待返回的出站发送消息时调度入站发送消息(何时可以线程接收窗口消息?)。 - IInspectable

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接