我怎样才能使一个subprocess窗口在我的过程中出现模态?

我有一个应用程序调用其他实用程序应用程序来为特定设备设置一些设置。 该实用程序应用程序使用ShellExecuteEx进行调用。

为了不使用户混淆,最好将实用程序模式窗口设置为我的主窗口。 如何做到这一点?

我试过的东西:

  1. WaitForSingleObjectEx在ShellExecuteEx之后的进程中,INFINITE TIMEOUT – 窗口是模态的,但主应用程序不重画(因为它正在等待单个对象!)
  2. WaitForSingleObjectEx在ShellExecuteEx之后的过程中,有一些小超时,然后调用PeekMessage和DispatchMessage – repaint现在可以工作,但是实用程序应用程序不再是“模态”的。 主应用程序响应鼠标点击,button点击等
  3. EnableWindow(FALSE),然后执行方法#2,然后EnableWindow(TRUE) – WORKS !!!,但是在此之后,我的应用程序的z顺序改变了。 (现在在其他窗口下面)。 为什么?!

你有两件事要模拟:所有权和形式。

要模拟所有权:您需要将新的子进程窗口的所有者设置到您的窗口。 这应该减轻任何z订购问题。 虽然我不知道这是否从另一个过程。 如果没有,那么你可能不得不附加你的线程输入队列,然后调用它。 或者使用其他一些代码注入技术。

SetWindowLong <target window handle>, GWL_HWNDPARENT, <new owner handle> 

为了模拟模态,我认为你使用EnableWindow和WaitForSingleObjectEx是正确的。

简单的答案是,即使线程处于相同的进程中,也无法在线程A中的窗口中无缝地创建线程B模式的窗口。 如果你拥有这两个窗口的代码,你可能会接近,但在这种情况下,通过将所有的UI放在一个线程中,你将获得更好的结果。

如果您尝试向用户建议线程B的窗口对于线程A是模态的,那么您必须获得许多微妙的Z顺序和激活行为(正如您已经注意到的),以免您遭受到不可思议的谷歌效应对用户来说很清楚,线程B的窗口正试图成为它不是的东西,因此似乎被破坏了。

为了避免这一点,我会采取这种做法:

  1. 用户点击canner.exe主窗口中的“FDA检查”。 canner.exe显示一个模式对话框,指示它正在打开一个外部程序(“打开Botulism设置…”)。 这将禁用主窗口等,以便用户知道模态交互正在发生。
  2. canner.exe调用ShellExecuteEx ()启动botulism.exe。
  3. canner.exe从ShellExecuteEx()返回的句柄上调用WaitForInputIdle ()。 当botulsim.exe准备好用于用户交互时,WaitForInputIdle()将返回(大概,但通常足够接近)。 如果botulism.exe通常需要五秒或更长时间才能显示其UI,则可以在循环中使用WaitforInputIdle()的短暂超时,并使用PeekMessage()/ ProcessMessage()偶尔处理所有待处理的消息。
  4. canner.exe更改其对话框文本,以反映它正在等待用户关闭botulism.exe(“关闭Botulism设置以继续…”)。
  5. canner.exe在循环中调用MsgWaitForMultipleObjects (),直到botulsim.exe关闭。 MsgWaitForMultipleObjects()将在传递的句柄发送信号时或线程队列中有消息等待时返回。
  6. 如果用户点击canner.exe模式对话框中的关闭框,而canner.exe正在等待,canner.exe提示用户botulism.exe仍在运行(“Botulism Settings仍然打开,继续吗?”,“是的,我知道“或”不,我没做完“)。 如果确认,canner.exe关闭对话框并取消在步骤1中开始的原始FDA检查,并返回到主窗口的消息循环。
  7. 当MsgWaitForMultipleObjects()表明botulism.exe完成时,canner.exe会关闭对话框,并在步骤1中开始FDA检查后正常继续。

这样,如果一切正常快速地进行,交互可能是无缝的,但是如果子进程出了问题或者Z顺序改变了,等等,这将清楚为什么父进程正在等待以及用户需要做或取消或继续他开始的任务。

EnableWindow是正确的,这通常是如何消息框和其他“模态”窗口。 至于zorder更改,您可以拦截WM_WINDOWPOSCHANGING消息并设置SWP_NOZORDER标志以防止zorder更改。 确保在设置EnableWindow时只做这个(false)。

只是合乎逻辑的建议,
也许你可以创建无形的模态形式 ,并从他使用方法#1。

让我们来看看你的方法#3,这是非常接近你想要的。 我怀疑问题是,当第二个应用程序关闭时,Windows决定不想将焦点还原到禁用的窗口。 在这种情况发生之前,你可以尝试重新启用你的窗口,但这很可能是棘手的(而且不值得付出)。

不要直接禁用窗口,只要忽略用户输入就可以禁用它。 因此,而不是调用EnableWindow,更改您的消息循环以筛选出输入消息。 特别是,如果

 msg >= WM_KEYFIRST || msg <= WM_KEYLAST || msg >= WM_MOUSEFIRST || msg <= WM_MOUSELAST 

然后丢弃该消息; 否则,将它传递给正常的调度循环。 你正在做的是创建自己的禁用窗口,但Windows不知道这一点。