This article is an example in Microsoft's Visual Basic that does something very similar to what you need.
They take the following approach:
- Mouse capture in
Form_MouseDown . - When the mouse moves, draw a rectangle around the window indicated by the mouse:
Form_MouseMove . - Release the mouse in
Form_MouseUp , and also cancel the entire screen to remove the last drawn rectangle.
They are drawn directly in the window that they select. I do not think that all flicker can be avoided using the transparent window approach.
This sample code seems incomplete and does not work, so I modified it (and translated it to Delphi):
// Not global variables, but private form ones var HwndLastTracked: HWND; CapturedMouse: boolean; procedure InvertTracker(hwndWindow: HWND); var rc: TRect; dc: HDC; pen, oldPen: HPEN; oldBrush: HBRUSH; style, exStyle: longint; cx, cy: integer; begin GetWindowRect(hwndWindow, rc); // Window coordinates of the origin (top-left corner) of a window is (0, 0) OffsetRect(rc, -rc.Left, -rc.Top); // DC returned by GetWindowDC covers the full window area, but in Windows // Vista/7 it seems to be clipped excluding the nonclient region, due to // DWM handling nonclient drawing, so it doesn't allow painting over it. // Thus we need to skip this nonclient area and that is why I adjust the // window rect to match the client area. Using GetClientRect instead of // GetWindowRect is not suitable as excludes scroll bars and child // parts drawed in WM_NCPAINT, such as Windows' WS_EXEDGEs and Delphi's // bevels. style := GetWindowLong(hwndWindow, GWL_STYLE); exStyle := GetWindowLong(hwndWindow, GWL_EXSTYLE); if style and WS_CAPTION <> 0 then begin if exStyle and WS_EX_TOOLWINDOW <> 0 then cy := GetSystemMetrics(SM_CYSMCAPTION) else cy := GetSystemMetrics(SM_CYCAPTION); // discard area covered by caption Inc(rc.Top, cy); end; if style and WS_THICKFRAME <> 0 then begin cx := GetSystemMetrics(SM_CXFRAME); cy := GetSystemMetrics(SM_CYFRAME); end else if style and WS_DLGFRAME <> 0 then begin cx := GetSystemMetrics(SM_CXDLGFRAME); cy := GetSystemMetrics(SM_CYDLGFRAME); end else if style and WS_BORDER <> 0 then begin cx := GetSystemMetrics(SM_CXBORDER); cy := GetSystemMetrics(SM_CYBORDER); end else begin cx := 0; cy := 0; end; if (cx <> 0) or (cy <> 0) then begin // discard area covered by borders OffsetRect(rc, cx, cy); Dec(rc.Right, cx*2); Dec(rc.Bottom, cy*2); end; // Windows API functions don't raise exceptions, so I don't use try-finally dc := GetWindowDC(hwndWindow); // Option 1: focused rect //DrawFocusRect(dc, rc); // Option 2: inverted thick border SetROP2(dc, R2_NOT); pen := CreatePen(PS_INSIDEFRAME, 3 * GetSystemMetrics(SM_CXBORDER), 0); oldPen := SelectObject(dc, pen); oldBrush := SelectObject(dc, GetStockObject(NULL_BRUSH)); Rectangle(dc, rc.Left, rc.Top, rc.Right, rc.Bottom); SelectObject(dc, oldBrush); SelectObject(dc, oldPen); DeleteObject(pen); // End option 2 ReleaseDC(hwndWindow, dc); end; procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if SetCapture(Handle) <> 0 then begin CapturedMouse := true; HwndLastTracked := 0; Screen.Cursor := crCross; end; end; procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); var hwndCaptured: HWND; begin if CapturedMouse then begin hwndCaptured := WindowFromPoint(ClientToScreen(Point(X, Y))); // Uncomment this for track root windows instead of childs //hwndCaptured := GetAncestor(hwndCaptured, GA_ROOT); if hwndCaptured <> HwndLastTracked then begin if HwndLastTracked <> 0 then InvertTracker(HwndLastTracked); InvertTracker(hwndCaptured); HwndLastTracked := hwndCaptured; end; end; end; procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if CapturedMouse then begin ReleaseCapture; CapturedMouse := false; if HwndLastTracked <> 0 then begin InvertTracker(HwndLastTracked); HwndLastTracked := 0; end; Screen.Cursor := crDefault; end; end;
Here's a screenshot of how Microsoft uses this technique in Visual Studio Spy ++. The red ball and the text are mine!
