Search code examples
winapigdi

How do I draw a rectangle on the desktop like UISpy/Spy++/etc..?


I'm playing around in a console application and thought it be nice if I could just popup a rectangle around areas on the desktop so I can visually see where various RECTs are. Like you see with things like UISpy, Inspect, Spy++ (drag target over window), etc.. What method is typically used to do that?

I tried this, but it is not working:

void DoMessageLoop(void * parg)
{
  MSG msg;

  // Main message loop:
  while (GetMessage(&msg, NULL, 0, 0))
  {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
  }
  _endthread();
}


HWND CreateLayeredWindow(RECT *prc)
{
 LPCTSTR szWindowClass = _T("TransparentClass");

    // Register class
    WNDCLASSEX wcex = {0};

    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.lpfnWndProc    = DefWindowProc;
    wcex.hInstance      = GetModuleHandle(NULL);
    wcex.lpszClassName  = szWindowClass;

    RegisterClassEx(&wcex);

    HWND hWnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOPMOST, szWindowClass, 0, WS_OVERLAPPEDWINDOW, prc->left, 
                               prc->top, prc->right-prc->left, prc->bottom-prc->top, NULL, NULL, GetModuleHandle(NULL), NULL);


    SetLayeredWindowAttributes(hWnd, RGB(255,0,255), 50, LWA_COLORKEY);

    HBRUSH brush=CreateSolidBrush(RGB(255, 0, 255));
    HPEN pen=CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
    HDC hdc=GetDC(hWnd);
    HBRUSH oldbrush=(HBRUSH)SelectObject(hdc, brush); 
    HPEN oldpen=(HPEN) SelectObject(hdc, pen);

    Rectangle(hdc, 0, 0, prc->right-prc->left, prc->bottom-prc->top);

    SelectObject(hdc, oldpen);
    SelectObject(hdc, oldbrush);

    DeleteObject(pen);
    DeleteObject(brush);

    ReleaseDC(hWnd, hdc);

    ShowWindow(hWnd, SW_SHOW);

    _beginthread(DoMessageLoop, 0, hWnd);

    return hWnd;
}

Solution

  • Okay, I got it working, but had to setup a WndProc (and move create/message loop to same thread):

    LRESULT DrawRedRect(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
    {
      static RECT rcarea;
    
      switch (msg) {
        case WM_CREATE:
        {
          CREATESTRUCT *cs=(CREATESTRUCT*) lp;
          rcarea=*((LPRECT) cs->lpCreateParams);
          return DefWindowProc(hwnd, msg, wp, lp);
        }
    
        case WM_PAINT:
        {
          PAINTSTRUCT ps {};
          HDC hdc=BeginPaint(hwnd, &ps);
    
          RECT rc {}; 
          GetClientRect(hwnd, &rc);
    
          _ASSERT((rc.right-rc.left) == (rcarea.right-rcarea.left));
          _ASSERT((rc.bottom-rc.top) == (rcarea.bottom-rcarea.top));
    
          HPEN hPen=CreatePen(PS_SOLID, 5, RGB(255, 0, 0));
          HBRUSH hBrush=CreateSolidBrush(MYTRANSPARENTCOLOR);
          HGDIOBJ hOldPen=SelectObject(hdc, hPen);
          HGDIOBJ hOldBrush=SelectObject(hdc, hBrush);
    
          Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
    
          if (hOldPen)
            SelectObject(hdc, hOldPen);
          if (hOldBrush)
            SelectObject(hdc, hOldBrush);
          if (hPen)
            DeleteObject(hPen);
          if (hBrush)
            DeleteObject(hBrush);
    
          EndPaint(hwnd, &ps);
        }
        break;
    
        case WM_DESTROY:
          PostQuitMessage(0);
          break;
    
        default:
          return DefWindowProc(hwnd, msg, wp, lp);
      }
    
      return 0;
    }
    
    //-----------------------------------------------------------------------------------
    
    HWND g_hWnd=NULL;
    
    void CreateLayeredWindowThread(void *parg) 
    {
      const PRECT &prc=reinterpret_cast<PRECT>(parg);
    
      _ASSERT(g_hWnd==NULL);
    
       LPCTSTR szWindowClass = _T("TransparentClass");
    
        // Register class
        WNDCLASSEX wcex = {0};
    
        wcex.cbSize = sizeof(WNDCLASSEX);
        wcex.lpfnWndProc    = DefWindowProc;
        wcex.hInstance      = GetModuleHandle(NULL);
        wcex.lpszClassName  = szWindowClass;
        wcex.lpfnWndProc=DrawRedRect;
    
        RegisterClassEx(&wcex);
    
        RECT winrect=*prc;
    
        AdjustWindowRect(&winrect, WS_POPUP, FALSE);
    
        g_hWnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW, szWindowClass, 0, WS_POPUP, prc->left, 
                                   winrect.top, winrect.right-winrect.left, winrect.bottom-winrect.top, NULL, NULL, GetModuleHandle(NULL), (LPVOID) prc);
    
    
    
        SetLayeredWindowAttributes(g_hWnd, MYTRANSPARENTCOLOR, 0, LWA_COLORKEY);
    
        ShowWindow(g_hWnd, SW_SHOW);
    
      MSG msg;
    
      // Main message loop:
      while (GetMessage(&msg, NULL, 0, 0))
      {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
      }
      _endthread();
    
    }
    
    //-----------------------------------------------------------------------------------
    
    void CreateLayeredWindow(RECT *prc)
    {
      _beginthread(CreateLayeredWindowThread, 0, prc);
    }