【问题标题】:Can a window be always on top of just one other window?一个窗口可以总是在另一个窗口之上吗?
【发布时间】:2009-05-01 18:46:39
【问题描述】:

在 Windows 中,是否可以将窗口 A 设置为始终位于窗口 B 的顶部,同时允许其他窗口正常工作并在活动时出现在两者的顶部。

换句话说,我想要两个窗口之间的父子关系。这可以在不使窗口 A 成为窗口 B 的子窗口(MDI 样式)的情况下完成吗?窗口 B 不是我的 (Internet Explorer),当我尝试使用 SetParent 来实现这一点时,我的对话框 A 的图形被搞砸了。

我以为我已经用MSDN forum post 的这个想法破解了它,但是唉,窗口 A 仍然总是在一切之上,而不仅仅是窗口 B。

// Place window A on top
SetWindowPos(hWndWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
// Place window B underneath it
SetWindowPos(hWndParent, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);

有可能吗?

【问题讨论】:

  • 很遗憾,我认为没有办法做到这一点。

标签: windows winapi


【解决方案1】:

创建所有权关系不会解决问题吗?

SetWindowLong(hwndChild, GWL_HWNDPARENT, hwndOwner)

窗口可以在不同的进程中,您可以从任何进程中调用它。这将确保子窗口始终位于所有者窗口之上。这与实际创建父/子关系的 SetParent 不同。 Read through this article(它来自 1993 年,但仍然大部分是正确的)来了解所有权和养育子女之间的区别。

【讨论】:

  • 链接显示Content Removed
  • 我试过GWL_HWNDPARENT,但是在Excel 2016窗口设置时,不能正常工作。
【解决方案2】:

当窗口的 Z 顺序(或大小或位置)发生变化时,它应该会收到 WM_WINDOWPOSCHANGING 消息。如果您处理该消息,您就有机会修改窗口移动到的最终 Z 顺序(或大小或位置)。

为了说明,在hWndA的窗口过程中:

case WM_WINDOWPOSCHANGING:
    DefWindowProc(hWnd, msg, wParam, lParam);
    WINDOWPOS *p = (WINDOWPOS*)lParam;
    p->hwndInsertAfter = hWndB;
    p->flags &= ~SWP_NOZORDER;
    return 0;

只要 hWndA 的位置发生变化,就应该在 Z 顺序中的 hWndB 之后插入 hWndA。

【讨论】:

  • Mat 不拥有父窗口,因此他无法更改“A”的 winproc。但是,也许某种类型的父挂钩会起作用?
  • 实际上,他说他拥有 A,并想让它看起来是 B 的孩子,而不是他的 (Internet Explorer)。我认为如果 B 移到前面,A 会收到 A 的 z 顺序发生变化的通知。
  • 是的!你让我今天一整天都感觉很好!我通过处理ON_WM_WINDOWPOSCHANGING() 得到我想要的,然后做void CMyFrame::OnWindowPosChanging(WINDOWPOS* lpwndpos) { __super::OnWindowPosChanging(lpwndpos); CMainFrame* pMainFrame = dynamic_cast<CMainFrame*>(AfxGetMainWnd()); if (pMainFrame) { lpwndpos->hwndInsertAfter= pMainFrame->GetSafeHwnd(); } }
  • 其他窗口下的底层点击激活hWndB时,hWndB上方的hWndA消失,其wndproc没有收到任何消息。
【解决方案3】:

在 Vista 之前,一种方法是使用 SetWindowsHookEx,并挂钩 WH_CBTWH_CALLWNDPROC 挂钩,然后在检测到 Z 顺序更改时采取适当的措施。但是,这不适用于 Vista(据我通过谷歌搜索得知)。

我能想到的唯一其他解决方案是设置一个每隔几秒触发一次的计时器,然后当您收到WM_TIMER 时,您使用GetNextWindow 询问系统以找出您的窗口后面是哪个窗口。如果不是 IE,则调用 SetWindowPos 将您的窗口定位在 IE 上方(我假设您有一个 HWND 用于您关心的 IE 窗口 - 请记住可以有多个 IE 窗口)。

如果人们试图将您的窗口放在前面,这将导致问题 - 它会翻转回 IE 上方。在这种情况下,在您的代码中,您可以处理WM_ACTIVATE 并尝试更改 IE 窗口的 Z 顺序,使其位于您的窗口下方(调用 SetWindowPos 移动 IE 的窗口,使其位于当前窗口下方的窗口上方) .此解决方案可能充满问题,因为出于安全原因,Windows 可能会尝试阻止您弄乱另一个进程的窗口。另一方面,SetWindowPos 的 MSDN 文档没有明确提到您不能操纵另一个进程的窗口。不过可能有一些模糊的限制。

即使使用此计时器技巧,您的应用程序中也会有效地产生一个忙等待循环(频繁出现 WM_TIMER 消息),这通常是一件糟糕的事情,尤其是对于笔记本电脑等的电池寿命(因为您阻止 CPU 进入睡眠状态等)。

我想说没有什么好的方法可以做到这一点,而且任何你可能会开始工作的东西都会很脆弱并会导致问题。 我强烈建议不要尝试这样做。是否可以将您的程序变成某种 IE 的插件或工具栏?

注意请特别注意SetWindowsHookEx 会在系统范围内施加性能损失,如果您采用此方法。

【讨论】:

    【解决方案4】:

    莫里斯的回答是最好的,但缺少重要的一步。当您在希望作为覆盖的窗口上调用 show 时,您需要调用具有参数的 show 方法。您需要定义一个实现 IWin32Window 接口的类,然后创建一个新的实例。界面唯一关心的是句柄,所以只需将它设置为 IE 窗口的句柄,它应该可以很好地工作

    【讨论】:

      【解决方案5】:

      如果父子关系是自己用SetWindowPos()函数建立的,你的愿望就可以实现了。

      【讨论】:

        【解决方案6】:

        你可以访问窗口的 Z 顺序吗?

        我不记得窗口的默认 z 顺序,但我认为它是 1。您可以将 IE 设置为 -1,将您的应用设置为 0。

        【讨论】:

          【解决方案7】:

          试试这个:

          // Place window A on top of window B
          SetWindowPos(hWndA, hWndB, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
          

          第二个窗口句柄参数指定按 Z 顺序向下的下一个窗口。

          请注意,这实际上并不会改变窗口父子关系 - 但您可以模拟它。

          【讨论】:

          • 它只做一次(但无论如何它只做一次)。单击 IE 会将其带到我的窗口前面:(
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多