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:

  1. Handle WM_NCHITTEST in window being dragged
  2. Return HTTRANSPARENT during dragging. Call default window proc otherwise.
  3. 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.
  4. Make sure there're no child windows at point passed to WindowFromPoint, or handle WM_NCHITTEST for these child windows as well.

Troubleshooting (if you still get your window from WindowFromPoint)

  1. Test GetCurrentThreadID() == GetWindowThreadProcessId(WindowFromPoint(), 0) to ensure you're calling from correct thread
  2. In WM_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().

Tags:

Windows

Winapi