简体   繁体   English

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

[英]launch another application as popup window

I have a MFC application that launches other (generic windowed, black box) applications as a pop-up and waits for their completion. 我有一个MFC应用程序,它会作为弹出窗口启动其他(通用窗口,黑盒)应用程序,并等待其完成。 No communication/interaction between parent and child is required and should be avoided. 父母与孩子之间不需要沟通/互动,应避免。 Only the "child app behaves as modal dialog of parent app" behavior is wanted. 仅需要“子应用程序表现为父应用程序的模式对话框”。 What is the correct way to do this? 正确的方法是什么?

An example of "launch another application as child window" can be seen at: Activating an application as a child/popup of another application which leads to http://www.codeproject.com/Articles/18724/Hosting-exe-applications-into-a-dialog (this is not what I want, I want modal popup behavior) 可以在以下位置看到“启动另一个应用程序作为窗口”的示例: 将应用程序激活为另一个应用程序的窗口/弹出窗口,从而导致http://www.codeproject.com/Articles/18724/Hosting-exe-applications-进入一个对话 (这不是我想要的,我想要模式弹出行为)

For simplicity we can assume that both launching and launched applications have single "stack" of windows (one main window with modal dialogs that can have own modal dialogs). 为简单起见,我们可以假设启动和启动的应用程序都具有单个“堆栈”窗口(一个带有模态对话框的主窗口可以具有自己的模态对话框)。

My current pseudo-code (error handling and callback function omitted for simplicity) 我当前的伪代码(为简单起见,省略了错误处理和回调函数)

//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);

Now what my lesser problems are with foreground visual style (eg foreground = blue titlebar vs. background = gray titlebar) and keyboard input focus behavior: 现在,我较小的问题是前景视觉样式(例如,前景=蓝色标题栏与背景=灰色标题栏)和键盘输入焦点行为:

1) initial removal of childs WS_APPWINDOW style removes foreground visual and input focus from child app. 1)最初删除子级WS_APPWINDOW样式将从子级应用程序中删除前景视觉和输入焦点。 No application has focus at that point. 在这一点上,没有应用程序具有焦点。

2) when user clicks any parent application window, the child foreground visual style is toggled. 2)当用户单击任何父应用程序窗口时,将切换子前景的视觉样式。 Keyboard focus is retained in the child application. 键盘焦点保留在子应用程序中。 example: child app has foreground + focus -> click parent 1st time -> child loses foreground, retains focus -> click parent 2nd time -> child gains foreground, keeps focus -> etc. 示例:子应用具有前景+焦点->第一次单击父项->子失去前景,保持焦点->单击父项第二次->子项获得前景,保持焦点->等等。

expected behavior (what "normal" MFC popups do): child app has foreground + focus -> click parent -> child titlebar flashes briefly and retains foreground + focus child app does not have foreground + focus -> click parent -> child titlebar gains foreground and keyboard focus 预期的行为(“常规” MFC弹出窗口会执行的操作):子应用程序具有前景+焦点->单击父项->子标题栏短暂闪烁并保留前景+焦点子应用程序没有前景+焦点->单击父项->子标题栏获得前景和键盘焦点

And now this is worst problem : 3) I have encountered a MFC application that when launched separately user can open a "stack of modal dialogs" A->B->C->D->E, the ownership of windows is exactly matching this (E is owned by D, D owned by C etc.). 现在这是最严重的问题 :3)我遇到了一个MFC应用程序,当单独启动该应用程序时,用户可以打开“模式对话框堆栈” A-> B-> C-> D-> E,Windows的所有权完全匹配(E由D拥有,D由C拥有,等等)。 But if I open it from my MFC application (M), the ownership looks like M->A->B->C,D,E (C,D,E are all owned by B, B owned by A, A owned by my app window M). 但是,如果我从MFC应用程序(M)中打开它,则所有权看起来就像M-> A-> B-> C,D,E(C,D,E都由B拥有,B由A拥有,A拥有通过我的应用窗口M)。 This leads to "stack without support" http://blogs.msdn.com/b/oldnewthing/archive/2005/02/24/379635.aspx problem. 这会导致“没有支持的堆栈” http://blogs.msdn.com/b/oldnewthing/archive/2005/02/24/379635.aspx问题。 This behavior disappears when I remove SetWindowLong(childHwnd, GW_OWNER, parentHwnd) so messing with ownership probably triggers the unwanted behavior of child application, but without that I can't seem to guarantee the "one is positioned over the other" premise of modal dialog. 当我删除SetWindowLong(childHwnd, GW_OWNER, parentHwnd)时,此行为消失了SetWindowLong(childHwnd, GW_OWNER, parentHwnd)因此弄乱所有权可能会触发子应用程序的有害行为,但是如果没有这样做,我似乎无法保证模式对话框的前提是“一个位于另一个之上” 。

So the grand question again: what is the correct way to do this task and avoid the problems I have described. 因此,还有一个大问题:执行此任务并避免出现我所描述的问题的正确方法是什么?

Edit 编辑

Solution so far 到目前为止的解决方案

We must avoid messing with owner-owned structures as @mfc suggests below, so the task is basically to reimplement this aspect of window manager for just our parent-child pair in another way. 我们必须避免像@mfc所建议的那样弄乱所有者拥有的结构,因此,任务基本上是以另一种方式为我们的父子对重新实现窗口管理器的这一方面。 I have prototyped part of solution using Windows Hooks. 我已经使用Windows Hooks建立了解决方案的原型。 However it seems quite complex and tedious to complete, so I decided to go with another primitive approach (deadlines, oh deadlines). 但是,完成它似乎相当复杂且乏味,因此我决定采用另一种原始方法(截止日期,截止日期)。 For the sake of example, I will describe basic ideas of both. 为了举例,我将描述两者的基本思想。

Hook solution 挂钩解决方案

Disclaimer: only parent focus hook has been confirmed to work, rest is theorycrafting. 免责声明:只有父焦点挂钩已被确认可以正常工作,其余都是理论上的努力。 Maybe there is be cleaner/more lightweight implementation, one could get inspiration in actual Windows window manager implementation (remember the whole point is avoiding setting GW_OWNER which works fine for window manager, but can break the child black-box app behavior). 也许有一个更清洁/更轻量级的实现,可能会在实际的Windows窗口管理器实现中获得启发(记住整个要点是避免设置GW_OWNER,该功能对于窗口管理器来说很好用,但是会破坏子黑盒应用程序的行为)。

  • add some "ignore input while child is running" into parent message loop for when child app is running (intermittently) without windows 在子程序运行时(间歇地)无窗口的情况下,将一些“子程序运行时忽略输入”添加到父消息循环中
  • create shared memory and structures to hold [parentPid, parentHwnd,childPid] for each invocation 创建共享内存和结构以为每次调用保留[parentPid,parentHwnd,childPid]
  • create DLL instanced memory for [list of parent nonowned windows, their UI thread, child hook] 为[父非拥有窗口的列表,它们的UI线程,子钩子]创建DLL实例内存
  • hook systemwide to WH_CBT -> HCBT_CREATEWND, if childPid matches, register window in list, register another hook HCBT_ACTIVATE just for that child thread if not already present 将系统范围内的钩子链接到WH_CBT-> HCBT_CREATEWND,如果childPid匹配,则在列表中注册窗口,仅为该子线程注册另一个钩子HCBT_ACTIVATE(如果尚未存在)
  • hook systemwide to WH_CBT -> HCBT_DESTROYWND, if childPid matches, unregister window in list, unregister HCBT_ACTIVATE hook if this was last window for given thread, if this was last window for child app, unhook parent HCBT_ACTIVATE hook and focus parent 将系统范围内的钩子连接到WH_CBT-> HCBT_DESTROYWND,如果childPid匹配,则取消注册列表中的窗口,如果这是给定线程的最后一个窗口,则取消注册HCBT_ACTIVATE钩子,如果这是子应用程序的最后一个窗口,则取消钩子父级HCBT_ACTIVATE钩子并聚焦父级
  • parent thread HCBT_ACTIVATE hook prevents gaining focus and focuses child app instead using EnumWindows. 父线程HCBT_ACTIVATE挂钩会阻止获得焦点,而是使用EnumWindows来聚焦子应用程序。
  • child thread HCBT_ACTIVATE hooks prevent focus loss if the target is parent, keep parent just below child in Z-order 如果目标是父对象,则子线程HCBT_ACTIVATE挂钩可防止焦点丢失,以Z顺序将父对象保持在子对象下方
  • create child process suspended and resume only when hooks are in place 创建挂起的子进程并仅在挂钩到位时恢复
  • remember to unhook everywhere 记得到处解钩

Primitive approach 原始方法

basically applying focus switch in first point of previous solution, when parent is clicked it flickers over child as focus is transferred back and forth. 基本上在先前解决方案的第一点上应用了焦点开关,当单击父对象时,它会在子对象之间来回闪烁,从而在子对象上闪烁。

  • "ignore input while child is running" (discard various keypress, click, etc. messages) in parent message loop for when child app is running, focus child app instead using EnumWindows. 对于子应用程序运行时的父消息循环中的“忽略子项运行时忽略输入”(丢弃各种按键,单击等消息),请使用EnumWindows代替子应用程序。

Changing parent/child relationship of other windows that does not belong to you is tricky and error prone. 更改不属于您的其他窗口的父/子关系非常棘手且容易出错。 If the launcher program has no communication with the launched program and the main purposes is to avoid any UI with the launcher, you can simply hide the launcher after the secondary program is launched successfully using ShowWindow(SW_HIDE). 如果启动器程序与启动的程序没有通信,并且主要目的是避免与启动器有任何UI,则可以使用ShowWindow(SW_HIDE)在成功启动辅助程序后简单地隐藏启动器。 In hidden mode, it continues to monitor the launched program and un-hide itself when the secondary program terminates. 在隐藏模式下,它将继续监视启动的程序,并在辅助程序终止时取消隐藏自身。

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

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

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