Найти все окна под точкой

Я хочу найти все окна верхнего уровня (дочерние элементы рабочего стола) под заданной точкой на рабочем столе. Я не могу найти API для этого.

Мой сценарий заключается в том, что я перетаскиваю окно через экран и хочу поместить его в другое (известное) окно. Я могу нажать, чтобы проверить границы целевого окна, но это не говорит мне, закрыто ли оно другим (неизвестным) окном. С помощью WindowFromPoint и друзья не будут работать, потому что перетаскиваемое окно обязательно находится прямо под мышью. Поэтому мне интересно, смогу ли я получить все окна в позиции мыши и просмотреть их, чтобы увидеть, находится ли одно из отслеживаемых окон непосредственно под окном, которое я перетаскиваю.

Есть ли способ сделать это, не прибегая к EnumDesktopWindows/GetWindowRect на каждое перетаскивание мыши? Или возможно есть другое решение, которое я пропускаю.

2 ответа

Решение

Если вы любезно спросите, WindowFromPoint проигнорирует ваше окно (которое в данный момент перетаскивается) и вернет следующее окно. Это то, что Internet Explorer делает, когда вы перетаскиваете вкладку.

Для этого:

  1. Справиться WM_NCHITTEST в окно тащат
  2. Вернуть HTTRANSPARENT во время перетаскивания. В противном случае вызовите процедуру окна по умолчанию.
  3. WindowFromPoint будет игнорировать HTTRANSPARENT окна, но только те, которые принадлежат вызывающему потоку. Это не должно быть проблемой для вас, потому что вы должны звонить WindowFromPoint в любом случае из окна владельца темы.
  4. Убедитесь, что нет дочерних окон в точке, переданной WindowFromPointили ручкой WM_NCHITTEST и для этих дочерних окон.

Устранение неполадок (если вы по-прежнему получаете окно от WindowFromPoint)

  1. Тестовое задание GetCurrentThreadID() == GetWindowThreadProcessId(WindowFromPoint(), 0) чтобы убедиться, что вы звоните из правильного потока
  2. В WM_NCHITTESTпроверить это hwnd параметр равен тому, что вы получаете от WindowFromPoint()

Пример (область внутри прямоугольника возвращает основное окно из 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);
}

Правильно, вы уже знаете, что должен возвращать WindowFromPoint (), именно тот, который вы перетаскиваете. Затем используйте GetWindow() с uCmd = GW_HWNDNEXT, чтобы получить нижний в Z-порядке. GetWindowRect(), чтобы получить его границы, IntersectRect(), чтобы вычислить перекрытие.

Продолжайте вызывать GetWindow(), чтобы найти больше окон, которые могут перекрываться. Пока не вернется NULL или перекрытие не будет достаточно хорошим. Если нет, то вы, как правило, предпочитаете тот, который имеет наибольший прямоугольник результата из IntersectRect().

Другие вопросы по тегам