windows 一个窗口可以总是位于另一个窗口之上吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/812686/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 12:23:35  来源:igfitidea点击:

Can a window be always on top of just one other window?

windowswinapi

提问by Mat

In Windows, is it possible to set window A such that it is always on top of window B, yet allow other windows to work as normal and appear over the top of both, when active.

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

In other words, I want a parent-child relationship between two windows. Can this be done without making window A a child of window B, MDI-style? Window B isn't mine (Internet Explorer), and screws my dialog A's graphics up when I try to achieve this with SetParent.

换句话说,我想要两个窗口之间的父子关系。这可以在不使窗口 A 成为窗口 B 的子窗口的情况下完成吗,MDI 风格?窗口 B 不是我的(Internet Explorer),当我尝试使用SetParent.

I thought I'd cracked it with this idea from an MSDN forum post, but alas windows A is still always on top of everything, not just window B.

我以为我已经用MSDN 论坛帖子中的这个想法破解了它,但唉,windows 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);

Is it possible?

是否可以?

回答by Maurice Flanagan

Wouldn't creating an ownership relationship do the trick?

建立所有权关系不会奏效吗?

SetWindowLong(hwndChild, GWL_HWNDPARENT, hwndOwner)

The windows can be in different processes and you can call this from any process. This will ensure that the child window is always above the owner window. This is different than SetParent which actually creates a Parent / Child relationship. Read through this article(its from 1993 but still mostly correct) to see the distinction between ownership and parenting.

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

回答by Matthew Xavier

When your window's Z-order (or size or position) is changing, it should receive a WM_WINDOWPOSCHANGINGmessage. If you process that message, you have an opportunity to modify the final Z-order (or size or position) to which the window is moved.

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

To illustrate, in hWndA's window procedure:

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

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

should insert hWndA after hWndB in the Z-order any time hWndA's position changes.

任何时候 hWndA 的位置发生变化时,都应该在 Z 顺序中的 hWndB 之后插入 hWndA。

回答by Slacker

Until Vista, one way to do it would have been to use SetWindowsHookEx, and hook the WH_CBTor WH_CALLWNDPROChook, and then take appropriate action when you detect the Z order changing. However this doesn't work with Vista (as far as I can tell from googling).

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

The only other solution I can think of is to set up a timer to fire every few seconds, and then when you receive a WM_TIMER, you interrogate the system using GetNextWindowto find out which window is behind yours. If it's not IE, then call SetWindowPosto position your window above IE (I assume you have a HWNDfor the IE window you care about - remember there can be multiple IE windows).

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

This will cause problems if people try to bring your window to the front - it will flip back to being just above IE. In this case, in your code you could handle WM_ACTIVATEand try to change the Z-order of IE's window so it's below your window (call SetWindowPosto move IE's window so it's above the window that is currently below your window). This solution may be fraught with problems as Windows may try to prevent you messing with the windows of another process, for security reasons. On the other hand, the MSDN docs for SetWindowPosdon't explicitlymention that you can't manipulate the windows of another process. There may be obscure limitations though.

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

Even with this timer hack, you're going to effectively have a busy-waiting loop in your app (with the frequent WM_TIMER messages) and this is generally a badthing to do, especially for battery life of laptops etc. (because you prevent the CPU from entering a sleep state, and so on).

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

I'd say there's no good way of doing this, and anything you're likely to get working will be brittle and cause problems. I strongly recommend not trying to do it.Is it possible to make your program into some kind of plug-in or toolbar for IE instead?

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

NBBe particularly aware that SetWindowsHookEximposes a performance penalty at a system-wide level if you go down this route.

NB请特别注意,SetWindowsHookEx如果您沿着这条路线走下去,会在系统范围内施加性能损失。

回答by Ready Cent

Maurice's answer is the best out of what's here but is missing an important step. When you call show on your window that you want as the overlay, you need to call the show method that has the parameter. You'll need to define a class that implements the IWin32Window interface and just make a new instance of that. The only thing that interface cares about is the Handle so just set that to the handle of the IE window and it should work pretty well

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

回答by Ready Cent

If the parent-child relationship is made by yourself with the SetWindowPos() function, your desire can be implemented.

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

回答by kdevine

Can you access the Z-order of the windows?

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

I cannot recall the default z-order of windows, but I think it is 1. You might be able to set IE to a -1 and your app to 0.

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

回答by snowcrash09

Try this:

尝试这个:

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

The second window handle parameter specifies the next window down in the Z order.

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

Note this doesn't actually change the window parent-child relationships - but you can simulate it.

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