I want to find all the top-level windows (children of the desktop) beneath a given point on the desktop. I can't find an API for this.
My scenario is that I'm dragging a window across the screen and want to drop it into another (known) window. I can hit test the bounds of the target window ok, but that doesn't tell me whether it's occluded by another (unknown) window. Using WindowFromPoint
and friends won't work, because the window being dragged is necessarily directly under the mouse. So I'm wondering if I can obtain all windows at the mouse position, and review them to see whether one of the windows I'm tracking is directly beneath the window I'm dragging.
Is there a way to do this without resorting to EnumDesktopWindows
/GetWindowRect
on every mouse drag? Or perhaps there's another solution I'm missing.
If you ask kindly, WindowFromPoint
will ignore your window (the one currently being dragged) and return the next window. This is what Internet Explorer does when you drag a tab.
To do that:
WM_NCHITTEST
in window being draggedHTTRANSPARENT
during dragging. Call default window proc otherwise.WindowFromPoint
will ignore HTTRANSPARENT
windows, but only those belonging to the calling thread. This shouldn't be a problem for you, because you should be calling WindowFromPoint
from the window owner thread anyway.WindowFromPoint
, or handle WM_NCHITTEST
for these child windows as well.Troubleshooting (if you still get your window from WindowFromPoint
)
GetCurrentThreadID() == GetWindowThreadProcessId(WindowFromPoint(), 0)
to ensure you're calling from correct threadWM_NCHITTEST
, test that hwnd
parameter equals what you get from WindowFromPoint()
Example (the area within rectangle returns the underlying window from WindowFromPoint
):
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static const RECT s_TransparentRect = {100, 100, 200, 200};
switch (message)
{
case WM_NCCREATE:
SetTimer(hWnd, 1, 100, 0);
break;
case WM_TIMER:
{
POINT cursorPos;
GetCursorPos(&cursorPos);
TCHAR buffer[256];
_snwprintf_s(buffer, _countof(buffer), _TRUNCATE, _T("WindowFromPoint: %08X\n"), (int)WindowFromPoint(cursorPos));
SetWindowText(hWnd, buffer);
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
Rectangle(ps.hdc, s_TransparentRect.left, s_TransparentRect.top, s_TransparentRect.right, s_TransparentRect.bottom);
EndPaint(hWnd, &ps);
}
break;
case WM_NCHITTEST:
{
POINT cursorPos;
GetCursorPos(&cursorPos);
MapWindowPoints(HWND_DESKTOP, hWnd, &cursorPos, 1);
if (PtInRect(&s_TransparentRect, cursorPos))
return HTTRANSPARENT;
}
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
Right, you already know what WindowFromPoint() is going to return, should be the one you are dragging. Then use GetWindow() with uCmd = GW_HWNDNEXT to get the one below it in the Z-order. GetWindowRect() to get its bounds, IntersectRect() to compute the overlap.
Keep calling GetWindow() to find more windows that might be overlapped. Until it returns NULL or the overlap is good enough. If not then you'll normally favor the one that has the largest result rectangle from IntersectRect().
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With