如何跨进程管理前台窗口?
我有两个项目作为单独的进程运行,但属于同一应用程序:
- Master(包含
TMasterMainForm
和TMasterModalForm
) - Slave(包含
TSlaveForm
)
使用此应用程序的典型方法如下:
- Master 启动并显示
TMasterMainForm
。 - 用户可以通过单击
TMasterMainForm
中的按钮来运行从属设备。 - Master启动Slave进程。
- Slave 显示
TSlaveForm
。 - Master将
TMasterMainForm
的TForm.Handle
发送给Slave。 (通过 IPC = WM_COPYDATA)
第 5 步完成,以便当 Slave 关闭时,它可以将前台窗口设置回 TMasterMainForm
。这样做是为了改善用户体验。
在我们引入 TMasterModalForm
之前,这种方法一直运行良好。
TMasterModalForm
可以使用 TMasterMainForm
中的另一个按钮启动。它是一个单独的窗口,但以模式方式显示,并且将 TMasterMainForm
作为其显式弹出父窗口。
现在,当TSlaveForm
关闭时,从应用程序在TMasterMainForm
的句柄上调用SetForegroundWindow
,但这不再正确,因为有一个模式表单(TMasterModalForm
)位于其顶部。
所以问题是:
在这种不平凡的情况下如何管理设置前景窗口?
PS:这是一个简化的描述,真正的应用程序也以相反的方式执行前台窗口的操作。
I have two projects which run as separate processes, but belong to the same application:
- Master (contains
TMasterMainForm
andTMasterModalForm
) - Slave (contains
TSlaveForm
)
The typical way to use this application is like this:
- Master is started and shows the
TMasterMainForm
. - The user can run the slave by clicking a button in
TMasterMainForm
. - Master starts the Slave process.
- Slave shows the
TSlaveForm
. - Master sends the
TForm.Handle
ofTMasterMainForm
to Slave. (via IPC = WM_COPYDATA)
Step 5 is done so that when Slave is closing it can set the foreground window back to TMasterMainForm
. This is done to improve the user experience.
This worked fine until we introduced TMasterModalForm
.
TMasterModalForm
can be started using another button in TMasterMainForm
. It is a separate window, but is shown modal and has the TMasterMainForm
as its explicit popup parent.
Now when the TSlaveForm
is closed the Slave application calls SetForegroundWindow
on the handle of the TMasterMainForm
, but this is not correct anymore since there is a modal form (TMasterModalForm
) on top of it.
So the question is:
How do I manage setting the foreground window in this non trivial situation?
PS: This is a simplified description, the real application is also doing this foreground window stuff the other way around.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您无法将前台窗口设置为主主窗体,因为它已被禁用。它被禁用,因为您正在显示一个模式表单,其所有者是该主表单。
显而易见的解决方案是将前景窗口设置为模式窗体而不是主窗体。由于您的从属应用程序可能无法轻易知道哪个窗口处于活动状态,因此您应该重新设计 IPC,以允许从属应用程序询问主应用程序哪个窗口处于活动状态,然后将其设为前台窗口。
更优雅的解决方案是让主应用程序调用
SetForegroundWindow
。事实上,从主进程调用Application.BringToFront
可能会更简单。当然,从进程仍然需要向主进程发送消息才能调用它。难题的最后一部分是处理焦点窃取限制,但您可以使用AllowSetForegroundWindow
。您需要从属进程通过传递主进程的 ID 来调用此函数。You cannot set the foreground window to the master main form because it is disabled. It is disabled because you are showing a modal form whose owner is that main form.
The obvious solution is to set the foreground window to be the modal form rather than the main form. Since your slave app presumably cannot easily know which window is active, you should re-work your IPC to allow the slave app to ask the master app which window is active, and then make that the foreground window.
A more elegant solution would be to let the master app call
SetForegroundWindow
. In fact it would probably be simpler just to callApplication.BringToFront
from the master process. Of course, the slave process would still need to send a message to the master in order to invoke this. The final piece in the puzzle is dealing with the focus-stealing restrictions but you can do that usingAllowSetForegroundWindow
. You need your slave process to call this passing the ID of the master process.为了解决您的问题,我不会从从属应用程序调用 SetForegroundWindow,而是向主应用程序发送自定义激活消息。在此消息中,您可以在 WParam 或 LParam 中传递所需的句柄。主应用程序本身可以确定是否可以激活该句柄,或者需要激活模态表单。您将激活正确窗口的责任放在应用程序本身中。
PS:您不需要 WM_COPYDATA 来发送句柄。您只需传入您编写的任何消息的 WParam 或 LParam 即可。正如我上面所解释的,这适用于发送回的句柄,也适用于首先发送到从属设备的句柄。
To solve your problem, I would not call SetForegroundWindow from the slave application, but instead send a custom activate message to the master application. In this message you can pass the desired handle in WParam or LParam. The master application itself can determine whether it can activate that handle, or need to activate the modal form instead. You put the responsibility of activating the right window in the application itself.
PS: You don't need WM_COPYDATA to send a handle. You can just pass in in WParam or LParam of any message you make up. This goes for the handle to send back as I explained above, but also for the handle that is sent to the slave in the first place.