首页 文章

启动另一个应用程序作为弹出窗口

提问于
浏览
2

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

“启动另一个应用程序作为子窗口”的示例可以在以下位置看到:Activating an application as a child/popup of another application导致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);

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

1)初始删除子项WS_APPWINDOW样式从子应用程序中删除前景视觉和输入焦点 . 在那一点上,没有应用程序有重点 .

2)当用户点击任何父应用程序窗口时,切换子前景视觉样式 . 键盘焦点保留在子应用程序中 . 示例:子应用程序具有前景焦点 - >单击父级第一次 - >子级 loses 前景,保留焦点 - >单击父级第二次 - >子级 gains 前景,保持焦点 - >等 .

预期的行为(MFC弹出窗口做什么):子应用程序具有前景焦点 - >单击父 - >子 Headers 栏 flashes briefly 并保留前景焦点子应用程序没有前景焦点 - >单击父 - >子 Headers 栏 gains foreground 和键盘焦点

现在这是 worst problem :3)我遇到了一个MFC应用程序,当单独启动时,用户可以打开"stack of modal dialogs" A-> B-> C-> D-> E,windows的所有权与此完全匹配(E属于D,D由C等拥有) . 但是,如果我从我的MFC应用程序(M)打开它,所有权看起来像M-> A-> B-> C,D,E(C,D,E全部归B,B归B所有,A拥有通过我的应用程序窗口M) . 这导致"stack without support" http://blogs.msdn.com/b/oldnewthing/archive/2005/02/24/379635.aspx问题 . 当我删除 SetWindowLong(childHwnd, GW_OWNER, parentHwnd) 时,这种行为就会消失,因此搞乱所有权可能会触发子应用程序的不需要的行为,但如果没有这个,我似乎无法保证模态对话框的前提 .

所以这个重大问题:完成这项任务的正确方法是什么,避免我所描述的问题 .

编辑

到目前为止

解决方案

我们必须避免弄乱所有者拥有的结构,如下面的@mfc所示,所以任务基本上是以另一种方式重新实现窗口管理器的这个方面 . 我使用Windows Hooks制作了部分解决方案的原型 . 然而,完成它似乎相当复杂和繁琐,所以我决定采用另一种原始方法(截止日期,哦截止日期) . 为了举例,我将描述两者的基本思想 .

钩子解决方案

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

  • 将一些"ignore input while child is running"添加到父消息循环中,以便在没有窗口的情况下运行(间歇性)子应用程序

  • 为每次调用创建共享内存和结构以保存[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顺序中将父级保持在子级之下

  • 创建子进程暂停并仅在挂钩到位时恢复

  • 记得解开到处

原始方法

基本上在前一个解决方案的第一点应用焦点切换,当点击父节点时,它会随着焦点来回传递而在儿童上闪烁 .

父子消息循环中的

  • "ignore input while child is running"(丢弃各种按键,点击等消息)当子应用程序正在运行时,使用EnumWindows重点关注子应用程序 .

2 回答

  • 0

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

  • -2

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

相关问题