Find all windows beneath a point
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:
- Handle
WM_NCHITTEST
in window being dragged - Return
HTTRANSPARENT
during dragging. Call default window proc otherwise. WindowFromPoint
will ignoreHTTRANSPARENT
windows, but only those belonging to the calling thread. This shouldn't be a problem for you, because you should be callingWindowFromPoint
from the window owner thread anyway.- Make sure there're no child windows at point passed to
WindowFromPoint
, or handleWM_NCHITTEST
for these child windows as well.
Troubleshooting (if you still get your window from WindowFromPoint
)
- Test
GetCurrentThreadID() == GetWindowThreadProcessId(WindowFromPoint(), 0)
to ensure you're calling from correct thread - In
WM_NCHITTEST
, test thathwnd
parameter equals what you get fromWindowFromPoint()
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().