繁体   English   中英

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

[英]launch another application as popup window

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

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

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

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

//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,Windows的所有权完全匹配(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)时,此行为消失了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代替子应用程序。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM