查找点下方的所有窗口

时间:2016-05-25 20:08:51

标签: windows winapi

我想找到桌面上给定点下方的所有顶级窗口(桌面的孩子)。我找不到这个API。

我的情况是我在屏幕上拖动一个窗口并希望将其放入另一个(已知)窗口。我可以测试目标窗口的边界确定,但这并不能告诉我它是否被另一个(未知)窗口遮挡。使用WindowFromPoint和朋友将无法正常工作,因为被拖动的窗口必须直接在鼠标下方。所以我想知道我是否可以在鼠标位置获取所有窗口,并查看它们以查看我正在跟踪的其中一个窗口是否正在我正在拖动的窗口下方。

有没有办法在不按每个鼠标拖动EnumDesktopWindows / GetWindowRect的情况下执行此操作?或许还有另一种我不知道的解决方案。

2 个答案:

答案 0 :(得分:4)

如果你善意地问,WindowFromPoint将忽略你的窗口(当前被拖动的窗口)并返回下一个窗口。这是Internet Explorer在拖动选项卡时执行的操作。

要做到这一点:

  1. 在拖动的窗口中处理WM_NCHITTEST
  2. 在拖动过程中返回HTTRANSPARENT。否则调用默认窗口proc。
  3. WindowFromPoint will ignore HTTRANSPARENT个窗口,但只有那些属于调用线程的窗口。这对您来说应该不是问题,因为您应该从窗口所有者线程调用WindowFromPoint
  4. 确保在传递给WindowFromPoint的点上没有子窗口,或者也为这些子窗口处理WM_NCHITTEST
  5. 疑难解答(如果您仍然可以从WindowFromPoint获取窗口)

    1. 测试GetCurrentThreadID() == GetWindowThreadProcessId(WindowFromPoint(), 0)以确保您正在通过正确的主题进行调用
    2. WM_NCHITTEST中,测试hwnd参数等于WindowFromPoint()
    3. 的参数

      示例(矩形内的区域从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);
      }
      

答案 1 :(得分:3)

是的,您已经知道WindowFromPoint()要返回的内容,应该是您要拖动的内容。然后使用GetWindow()和uCmd = GW_HWNDNEXT在Z顺序中获取它下面的那个。 GetWindowRect()得到它的边界,IntersectRect()来计算重叠。

继续调用GetWindow()以查找可能重叠的更多窗口。直到它返回NULL或重叠足够好。如果没有,那么你通常会喜欢具有IntersectRect()最大结果矩形的那个。