17

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.

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.

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.

// 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?

Mat
  • 82,161
  • 34
  • 89
  • 109

7 Answers7

16

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.

Deanna
  • 23,876
  • 7
  • 71
  • 156
Maurice Flanagan
  • 5,179
  • 3
  • 30
  • 37
4

When your window's Z-order (or size or position) is changing, it should receive a WM_WINDOWPOSCHANGING message. 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.

To illustrate, in hWndA's window procedure:

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.

Matthew Xavier
  • 2,108
  • 1
  • 13
  • 18
  • Mat doesn't own the parent window, so he can't change "A"'s winproc. However, maybe some type of hook of the parent would work? – Aardvark May 04 '09 at 17:45
  • Actually, he says he owns A and wants to make it seem a child of B, which is not his (Internet Explorer). I think that A will receive notice that A's z-order is changing if B is moved in front. – Matthew Xavier May 04 '09 at 20:16
  • 1
    Yes! You made my day! I got what I want by handling `ON_WM_WINDOWPOSCHANGING()` and then doing `void CMyFrame::OnWindowPosChanging(WINDOWPOS* lpwndpos) { __super::OnWindowPosChanging(lpwndpos); CMainFrame* pMainFrame = dynamic_cast(AfxGetMainWnd()); if (pMainFrame) { lpwndpos->hwndInsertAfter= pMainFrame->GetSafeHwnd(); } } ` – sergiol Nov 13 '17 at 17:22
  • When hWndB is clicked and activated from bottom layer under other windows, hWndA above hWndB disappears and no messages received in it's wndproc. – Acewind Aug 15 '20 at 06:52
2

Until Vista, one way to do it would have been to use SetWindowsHookEx, and hook the WH_CBT or WH_CALLWNDPROC hook, 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).

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 GetNextWindow to find out which window is behind yours. If it's not IE, then call SetWindowPos to position your window above IE (I assume you have a HWND for the IE window you care about - remember there can be multiple IE windows).

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_ACTIVATE and try to change the Z-order of IE's window so it's below your window (call SetWindowPos to 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 SetWindowPos don't explicitly mention that you can't manipulate the windows of another process. There may be obscure limitations though.

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 bad thing to do, especially for battery life of laptops etc. (because you prevent the CPU from entering a sleep state, and so on).

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?

NB Be particularly aware that SetWindowsHookEx imposes a performance penalty at a system-wide level if you go down this route.

Slacker
  • 460
  • 1
  • 4
  • 10
1

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

Ready Cent
  • 1,821
  • 3
  • 18
  • 25
1

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

0

Can you access the Z-order of the windows?

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.

kdevine
  • 83
  • 1
  • 8
0

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.

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

snowcrash09
  • 4,694
  • 27
  • 45
  • That does it the once (but then it does it the once without it anyway). Clicking on IE brings it in front of my window :( – Mat May 01 '09 at 20:30