我有一个应用程序调用其他实用程序应用程序来为特定设备设置一些设置。使用ShellExecuteEx调用该实用程序。
为了不混淆用户,最好将实用程序应用程序的窗口设置为我的主窗口。如何做到这一点?
我尝试过的事情:
答案 0 :(得分:4)
您需要模拟两件事:所有权和模态。
模拟所有权: 您需要将新子进程窗口的所有者设置为窗口。 这应该可以缓解任何z排序问题。 虽然我不知道这是否适用于其他过程。 如果没有,那么您可能必须附加线程输入队列然后调用它。 或者使用其他一些代码注入技术。
SetWindowLong <target window handle>, GWL_HWNDPARENT, <new owner handle>
为了模拟模态,我认为你使用EnableWindow和WaitForSingleObjectEx是正确的。
答案 1 :(得分:3)
简短的回答是,即使线程处于同一进程中,也无法在线程B模式中为线程A中的窗口无缝地创建窗口。如果您拥有两个窗口的代码,您可能会接近,但在这种情况下,您可以通过将所有UI放在一个线程中来获得更好的结果。
如果你试图向用户建议线程B的窗口是线程A的模态,那么你必须得到许多微妙的Z顺序和激活行为(正如你所注意到的),以免你遭受不可思议的行为 - 各种各样的山谷效应,用户可以清楚地看到线程B的窗口试图成为它不是的东西,因此看起来很破碎。
为避免这种情况,我会采用这种方法:
这样,如果一切正常且快速地进行,那么交互可能是无缝的,但是如果子进程出现问题或者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();
}