如何让子进程窗口在我的进程中显示模态?

时间:2009-03-05 03:46:49

标签: windows winapi

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

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

我尝试过的事情:

  1. ShellExecuteEx之后的进程上的WaitForSingleObjectEx,INFINITE TIMEOUT - 窗口是模态的,但是主应用程序没有重绘(因为它正在等待单个对象!)
  2. 在ShellExecuteEx之后的进程上WaitForSingleObjectEx,一些小的超时,然后调用Peekmessage和DispatchMessage - 重绘现在可以工作,但实用程序应用程序不再是“模态”。主应用程序响应鼠标点击,按钮点击等
  3. EnableWindow(FALSE),然后执行方法#2,然后执行EnableWindow(TRUE) - WORKS !!!,但在此之后,我的应用程序的z顺序发生了变化。 (它现在低于其他窗口)。为什么?!

6 个答案:

答案 0 :(得分:4)

您需要模拟两件事:所有权和模态。

模拟所有权: 您需要将新子进程窗口的所有者设置为窗口。 这应该可以缓解任何z排序问题。 虽然我不知道这是否适用于其他过程。 如果没有,那么您可能必须附加线程输入队列然后调用它。 或者使用其他一些代码注入技术。

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

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

答案 1 :(得分:3)

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

如果你试图向用户建议线程B的窗口是线程A的模态,那么你必须得到许多微妙的Z顺序和激活行为(正如你所注意到的),以免你遭受不可思议的行为 - 各种各样的山谷效应,用户可以清楚地看到线程B的窗口试图成为它不是的东西,因此看起来很破碎。

为避免这种情况,我会采用这种方法:

  1. 用户在canner.exe的主窗口中单击“FDA Inspection”。 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设置仍然打开,仍然继续?”,“是的,我知道“或”不,我没有完成“)。如果确认,canner.exe将关闭对话框并取消在步骤1中启动的原始FDA检查并返回主窗口的消息循环。
  7. 当MsgWaitForMultipleObjects()表示botulism.exe已完成时,canner.exe将关闭对话框并继续正常,并在步骤1中启动FDA检查。
  8. 这样,如果一切正常且快速地进行,那么交互可能是无缝的,但是如果子进程出现问题或者Z顺序发生了变化等,那么很明显为什么父进程在等待用户需要做什么来取消或继续他开始的任务。

答案 2 :(得分:2)

EnableWindow是正确的,这通常是消息框和其他“模态”窗口的方式。对于zorder更改,您可以拦截WM_WINDOWPOSCHANGING消息并设置SWP_NOZORDER标志以防止zorder更改。确保只在设置EnableWindow(false)时执行此操作。

答案 3 :(得分:1)

恰当的逻辑建议,
也许你可以创建隐形模态形式,并从他那里使用方法#1。

答案 4 :(得分:0)

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

不要直接禁用窗口,而是尝试通过忽略用户输入来禁用它。因此,不要调用EnableWindow,而是更改消息循环以过滤掉输入消息。特别是,如果

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

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

答案 5 :(得分:0)

试试这个。这会暂停调用者,直到子进程退出。

    private void btnChildApp_Click(object sender, EventArgs e)
    {
        Process p = Process.Start(@".\ChildApp.exe");
        p.WaitForExit();
    }