2

我有一个 MFC 应用程序,它以弹出窗口的形式启动其他(通用窗口、黑盒)应用程序并等待它们完成。父母和孩子之间不需要沟通/互动,应该避免。只需要“子应用程序表现为父应用程序的模态对话框”行为。这样做的正确方法是什么?

“作为子窗口启动另一个应用程序”的示例可以在以下位置看到:将应用程序作为另一个应用程序的子窗口/弹出窗口激活,这会导致http://www.codeproject.com/Articles/18724/Hosting-exe-applications- into-a-dialog(这不是我想要的,我想要模态弹出行为)

为简单起见,我们可以假设启动和启动的应用程序都有单一的“堆栈”窗口(一个带有模式对话框的主窗口,可以有自己的模式对话框)。

我当前的伪代码(为简单起见,省略了错误处理和回调函数)

//get the current MFC dialog of launcher program we are launching the other app from
parentWnd = AfxGetMainWnd()->GetActiveWindow(); 
parentHwnd = parentWnd->GetSafeHwnd(); //HWND

// launch child and retrieve basic info from PROCESSINFO structure
CreateProcess(childExecutable); // => childProcessHandle, childProcessId 

//get the "main" window of child application
EnumWindows(EnumProc_That_Retrieves_TopLevelWindow_With_childProcessId); // => childHwnd

//link the child window as popup
SetWindowLong(childHwnd, GW_OWNER, parentHwnd);

//disable input into parent window
parentWnd->EnableWindow(FALSE);

//remove taskbar entry for child
SetWindowLong(child, GWL_EXSTYLE, GetWindowLong(child, GWL_EXSTYLE)&~WS_APPWINDOW);

//now keep waiting for the child process termination and process parent messages (e.g. WM_PAINT)
while (MsgWaitForMultipleObjects(childProcessHandle and process QS_ALLINPUT) {
   while (PeekMessage(PM_NOREMOVE)) AfxGetApp()->PumpMessage();
}

//re-enable input into parent window
parentWnd->EnableWindow(TRUE);

现在我较小的问题是前景视觉风格(例如前景=蓝色标题栏与背景=灰色标题栏)和键盘输入焦点行为:

1) 初始移除子 WS_APPWINDOW 样式会移除子应用的前景视觉和输入焦点。此时没有应用程序具有焦点。

2) 当用户单击任何父应用程序窗口时,子前景视觉样式被切换。键盘焦点保留在子应用程序中。例如:子应用程序有前景 + 焦点 -> 第一次单击父级 -> 子级失去前景,保持焦点 -> 第二次单击父级 -> 子级获得前景,保持焦点 -> 等等。

预期行为(“正常”MFC 弹出窗口所做的):子应用程序有前景 + 焦点 -> 单击父项 -> 子标题栏短暂闪烁并保留前景 + 焦点子应用程序没有前景 + 焦点 -> 单击父项 -> 子标题栏增益前景和键盘焦点

现在这是最糟糕的问题:3)我遇到了一个 MFC 应用程序,当单独启动时,用户可以打开“模式对话框堆栈”A->B->C->D->E,窗口的所有权完全匹配this(E归D所有,D归C所有)。但如果我从我的 MFC 应用程序 (M) 打开它,所有权看起来像 M->A->B->C,D,E (C,D,E 都归 B 所有,B 归 A,A 归通过我的应用程序窗口 M)。这导致“没有支持的堆栈” http://blogs.msdn.com/b/oldnewthing/archive/2005/02/24/379635.aspx问题。当我删除时,这种行为会消失,SetWindowLong(childHwnd, GW_OWNER, parentHwnd)因此弄乱所有权可能会触发子应用程序的不良行为,但如果没有,我似乎无法保证“一个位于另一个之上”

所以又是一个大问题:完成这项任务并避免我所描述的问题的正确方法是什么。

编辑

到目前为止的解决方案

正如@mfc 建议的那样,我们必须避免弄乱所有者拥有的结构,因此任务基本上是以另一种方式为我们的父子对重新实现窗口管理器的这一方面。我已经使用 Windows Hooks 对解决方案的一部分进行了原型设计。然而,完成它似乎相当复杂和乏味,所以我决定采用另一种原始方法(截止日期,哦截止日期)。为了举例,我将描述两者的基本思想。

挂钩解决方案

免责声明:只有父焦点钩子已确认有效,其余为理论。也许有更清洁/更轻量级的实现,可以在实际的 Windows 窗口管理器实现中获得灵感(记住重点是避免设置 GW_OWNER 对窗口管理器工作正常,但会破坏子黑盒应用程序行为)。

  • 当子应用程序在没有窗口的情况下(间歇性地)运行时,将一些“在子程序运行时忽略输入”添加到父消息循环中
  • 为每次调用创建共享内存和结构来保存 [parentPid, parentHwnd,childPid]
  • 为 [父非拥有窗口列表、它们的 UI 线程、子挂钩] 创建 DLL 实例化内存
  • 将系统范围挂钩到 WH_CBT -> HCBT_CREATEWND,如果 childPid 匹配,则在列表中注册窗口,如果不存在,则仅为该子线程注册另一个挂钩 HCBT_ACTIVATE
  • 将系统范围挂钩到 WH_CBT -> HCBT_DESTROYWND,如果 childPid 匹配,则取消注册列表中的窗口,如果这是给定线程的最后一个窗口,则取消注册 HCBT_ACTIVATE 挂钩,如果这是子应用程序的最后一个窗口,取消挂钩父 HCBT_ACTIVATE 挂钩并聚焦父
  • 父线程 HCBT_ACTIVATE 钩子可防止获得焦点,而是使用 EnumWindows 聚焦子应用程序。
  • 如果目标是父线程,子线程 HCBT_ACTIVATE 挂钩可防止焦点丢失,在 Z 顺序中将父线程保持在子线程下方
  • 仅当挂钩到位时创建子进程暂停和恢复
  • 记得随处脱钩

原始方法

基本上在先前解决方案的第一点中应用焦点开关,当单击父级时,它会随着焦点来回转移而在子级上闪烁。

  • “在子应用程序运行时忽略输入”(丢弃各种按键、单击等消息)在子应用程序运行时的父消息循环中,使用 EnumWindows 聚焦子应用程序。
4

2 回答 2

0

更改不属于您的其他窗口的父/子关系非常棘手且容易出错。如果启动器程序与启动程序没有通信,主要目的是避免启动器的任何UI,您可以在辅助程序启动成功后使用ShowWindow(SW_HIDE)简单地隐藏启动器。在隐藏模式下,它会继续监视启动的程序,并在辅助程序终止时取消隐藏。

于 2012-08-19T01:43:11.930 回答
-3

尝试使用 API 函数“ShellExecute()”。

于 2015-01-17T08:06:13.270 回答