我有两个项目,它们作为单独的进程运行,但属于同一个应用程序:
- Master(包含
TMasterMainForm
和TMasterModalForm
) - 从(包含
TSlaveForm
)
使用此应用程序的典型方式如下:
- Master启动并显示
TMasterMainForm
- 用户可以通过点击
TMasterMainForm
中的一个按钮来运行从机 - Master启动Slave进程
- Slave显示
TSlaveForm
- Master向Slave发送
TMasterMainForm
的TForm.Handle
。(通过IPC=WM_COPYDATA)
完成步骤5,使得当Slave关闭时,它可以将前景窗口设置回TMasterMainForm
。这样做是为了改善用户体验。
在我们引入TMasterModalForm
之前,这一直很有效。
可以使用CCD_ 12中的另一个按钮来启动CCD_。它是一个单独的窗口,但显示为模态,并将TMasterMainForm
作为其显式弹出父窗口。
现在,当TSlaveForm
关闭时,从应用程序在TMasterMainForm
的句柄上调用SetForegroundWindow
,但这不再正确,因为它上面有一个模态形式(TMasterModalForm
)。
所以问题是:
在这种不重要的情况下,我该如何设置前台窗口?
附言:这是一个简化的描述,真正的应用程序也在以另一种方式做前台窗口的事情。
不能将前台窗口设置为主窗体,因为它已被禁用。它被禁用,因为您正在显示一个所有者是主窗体的模态窗体。
显而易见的解决方案是将前台窗口设置为模式窗口,而不是主窗口。由于您的从属应用程序可能无法轻易知道哪个窗口是活动的,因此您应该重新使用IPC,允许从属应用程序询问主应用程序哪个窗口是活动的,然后将其设置为前台窗口。
一个更优雅的解决方案是让主应用程序调用SetForegroundWindow
。事实上,只从主进程调用Application.BringToFront
可能会更简单。当然,从进程仍然需要向主进程发送一条消息才能调用它。难题的最后一块是处理焦点窃取限制,但您可以使用AllowSetForegroundWindow
来做到这一点。您需要您的从属进程通过主进程的ID来调用它。
为了解决您的问题,我不会从从属应用程序调用SetForegroundWindow,而是向主应用程序发送自定义激活消息。在该消息中,您可以在WParam或LParam中传递所需的句柄。主应用程序本身可以确定它是否可以激活该句柄,或者是否需要激活模态形式。您将激活正确窗口的责任放在了应用程序本身。
PS:您不需要WM_COPYDATA来发送句柄。你可以在WParam或LParam中传递你编造的任何消息。正如我上面解释的那样,这将用于发送回句柄,但也适用于首先发送到从属对象的句柄。