WinAPI : Starting with Device Context
When I first used Spy++ I was impressed by its Window Finder feature and always wanted to clone its functioning. My post Revealing Hidden Texts, which actually cloned the password revealers, had an implementation of Spy++ i.e. getting the handle to the Window. When you use Spy++ the window, that’s under your cursor, gets highlighted. This is what I will include with this post.
Let’s start with the form from previous post that I have modified slightly to delete unwanted code. Copy and paste the code to a text file and rename it to form.frm
The form contains three TextBoxes to store X and Y co-ordinates of the current cusror position and the handle to the window that is below the cursor. You need to first click on the form dragging cursor to the window, you need to know handle of.
VERSION 5.00
Begin VB.Form form1
Caption = "Form1"
ClientHeight = 3195
ClientLeft = 60
ClientTop = 345
ClientWidth = 4680
LinkTopic = "Form1"
ScaleHeight = 3195
ScaleWidth = 4680
StartUpPosition = 3 'Windows Default
Begin VB.TextBox txtHandle
Alignment = 1 'Right Justify
Height = 315
Left = 2640
TabIndex = 5
Top = 600
Width = 1815
End
Begin VB.TextBox txtYPos
Alignment = 1 'Right Justify
Height = 315
Left = 3360
TabIndex = 4
Top = 120
Width = 1095
End
Begin VB.TextBox txtXPos
Alignment = 1 'Right Justify
Height = 315
Left = 1920
TabIndex = 3
Top = 120
Width = 1095
End
Begin VB.Label Label4
Caption = "Window Handle"
Height = 255
Left = 1320
TabIndex = 6
Top = 600
Width = 1215
End
Begin VB.Label Label3
Caption = "Y"
Height = 255
Left = 3120
TabIndex = 2
Top = 120
Width = 255
End
Begin VB.Label Label2
Caption = "X"
Height = 255
Left = 1560
TabIndex = 1
Top = 120
Width = 255
End
Begin VB.Label Label1
Caption = "Mouse Position"
Height = 255
Left = 240
TabIndex = 0
Top = 120
Width = 1215
End
End
Attribute VB_Name = "form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Private Declare Function GetCursorPos Lib "user32" (ByRef lpPoint As POINT) As Long
Private Declare Function WindowFromPoint Lib "user32" (ByVal PosX As Long, _
ByVal PosY As Long) As Long
Private Type POINT
X As Long
Y As Long
End Type
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
Dim lpMousePos As POINT
GetCursorPos lpMousePos
txtXPos = lpMousePos.X
txtYPos = lpMousePos.Y
txtHandle = WindowFromPoint(lpMousePos.X, lpMousePos.Y)
End Sub
Now let’s get started to highlight the window. Once we have handle to the window we need to have the area that is aquired by the said window. The GetWindowRect function will help us in this.
Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Now when we get the RECT information of the window we need to draw a rectangle around it. Draw! where?
In Microsoft windows you don’t produce output on screen or paper, but use a virtual media, the Device Context, thus releaving you from necessity of knowing the hardware that’s you are working on. You write or draw everything on the device context which may be a printer, screen, a section on the screen, a memory location etc. Device Context in itself is a structure that has definitions to the area which could be written on, the brush, the pen, the text font that would be drawn on it. While will take details on Device Context later, for this post we will focus on the our requirement that is highlighting the window which is just below the mouse cursor.
To draw on a Device Context we require a “handle to the Device Context (hDC)” which can be achieved using the GetDC function which takes for input the Handle to the Window, of which we want the hDC. If the parameter to this functions is passed as NULL the handle to the DC of Complete Screen is returned, which would be sufficient for us. We would get this handle on load event of the form.
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Sub Form_Load()
lDC = GetDC(0)
End Sub
To highlight the rectangular area, we would use the DrawFocusRect function which will draw a dotted frame around the area. Since the frame drawn is thin we would draw another frame inflating the rectangle by 1 pixel on all sides using the InflateRect function
Private Declare Function InflateRect Lib "user32" (lpRect As RECT, _
ByVal x As Long, ByVal y As Long) As Long
Private Declare Function DrawFocusRect Lib "user32" (ByVal hdc As Long, lpRect As RECT) As Long

The rectangle drawn using the DrawFocusRect function can be erased by drawing once again on the same rectangle. To accomplish this we would need to save the handle to the previous window too. The variable defined below will hold the RECT structure of the window, handle to the Device Context and the Handle to the previous window resp.
Public objWinRect As RECT Public lDC As Long Public lStoredHandle As Long
The code below in the MouseMove event of the form will take care that after a handle to a window is received, if it’s other than the handle already stored the previous highlighting is removed and the new window is highlighted.
If txtHandle <> lStoredHandle Then
GetWindowRect lStoredHandle, objWinRect
DrawFocusRect lDC, objWinRect
InflateRect objWinRect, 1, 1
DrawFocusRect lDC, objWinRect
lStoredHandle = txtHandle
GetWindowRect txtHandle, objWinRect
DrawFocusRect lDC, objWinRect
InflateRect objWinRect, 1, 1
DrawFocusRect lDC, objWinRect
End If
The few lines as above added to the form will implement the highlighting of the window under the cursor. The code below is just re-written to include code from both posts
VERSION 5.00
Begin VB.Form form1
Caption = "Form1"
ClientHeight = 3195
ClientLeft = 60
ClientTop = 345
ClientWidth = 4680
LinkTopic = "Form1"
ScaleHeight = 3195
ScaleWidth = 4680
StartUpPosition = 3 'Windows Default
Begin VB.TextBox txtHandle
Alignment = 1 'Right Justify
Height = 315
Left = 2640
TabIndex = 5
Top = 600
Width = 1815
End
Begin VB.TextBox txtYPos
Alignment = 1 'Right Justify
Height = 315
Left = 3360
TabIndex = 4
Top = 120
Width = 1095
End
Begin VB.TextBox txtXPos
Alignment = 1 'Right Justify
Height = 315
Left = 1920
TabIndex = 3
Top = 120
Width = 1095
End
Begin VB.Label Label4
Caption = "Window Handle"
Height = 255
Left = 1320
TabIndex = 6
Top = 600
Width = 1215
End
Begin VB.Label Label3
Caption = "Y"
Height = 255
Left = 3120
TabIndex = 2
Top = 120
Width = 255
End
Begin VB.Label Label2
Caption = "X"
Height = 255
Left = 1560
TabIndex = 1
Top = 120
Width = 255
End
Begin VB.Label Label1
Caption = "Mouse Position"
Height = 255
Left = 240
TabIndex = 0
Top = 120
Width = 1215
End
End
Attribute VB_Name = "form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Private Declare Function GetCursorPos Lib "user32" (ByRef lpPoint As POINT) As Long
Private Declare Function WindowFromPoint Lib "user32" (ByVal PosX As Long, _
ByVal PosY As Long) As Long
Private Type POINT
x As Long
y As Long
End Type
Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function InflateRect Lib "user32" (lpRect As RECT, _
ByVal x As Long, ByVal y As Long) As Long
Private Declare Function DrawFocusRect Lib "user32" (ByVal hdc As Long, lpRect As RECT) As Long
Private objWinRect As RECT
Public lDC As Long
Public lStoredHandle As Long
Private Sub Form_Load()
lDC = GetDC(0)
End Sub
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
Dim lpMousePos As POINT
GetCursorPos lpMousePos
txtXPos = lpMousePos.x
txtYPos = lpMousePos.y
txtHandle = WindowFromPoint(lpMousePos.x, lpMousePos.y)
If txtHandle <> lStoredHandle Then
GetWindowRect lStoredHandle, objWinRect
DrawFocusRect lDC, objWinRect
InflateRect objWinRect, 1, 1
DrawFocusRect lDC, objWinRect
lStoredHandle = txtHandle
GetWindowRect txtHandle, objWinRect
DrawFocusRect lDC, objWinRect
InflateRect objWinRect, 1, 1
DrawFocusRect lDC, objWinRect
End If
End Sub

[...] — Jalaj @ 7:22 am We got a feel of what a Device Context is in a earlier post WinAPI : Starting with Device Context. Let’s explore [...]
Pingback by WinAPI : SetPixel Function « Jalaj — February 22, 2007 @ 7:24 am
[...] WinAPI : Starting with Device Context [...]
Pingback by Just Looking Back « Jalaj — May 28, 2007 @ 6:26 am
I just wanted to say WOW!.
Comment by Marilyn — September 15, 2007 @ 5:41 am
[...] The Blog Revisited - 5 By now I had jumped into using WinAPI function to the level I never imagined that I would get into… I always had some queries as how not to make rectangle forms etc that always got answer that can only happen on C++… was it totally true… NO that’s what I realized when dug into MSDN library and the answer was manipulating Device Context, the virtual device that we always write on to in Windows. For starting on it see WinAPI : Starting with Device Context [...]
Pingback by The Blog Revisited - 5 « Jalaj — October 30, 2007 @ 12:35 pm