UNKNOWN '************************************** ' Name: Window Selector (Like SPY++) ' Description:Here's a little bit of cod ' e that I'm using in one of my apps. I sl ' aved over it for 3 nights, so I thought ' that others might also like to use it. B ' asically, it Displays a bounding box aro ' und the window that the cursor is over, ' just like selecting a window in Microsof ' t SPY++. (I decompiled it and monitored ' it and used dependancies and all sorts o ' f junk from there too, in hope of imitat ' ing it). So here it is. This code snippet uses window regions, hDC's, System Objects, and basically complex drawing API's. It also shows how to clean up after yourself (I.e., Release memory correctly). Although it uses advanced techniques, you should get an idea of how graphics sort of work from here. Highly commented code, so as to maximise the knowledge that you can get from here. Please leave comments, as the feedback makes the program. If you like it, please vote :o) Jolyon Bloomfield February 2000 ' By: Jolyon Bloomfield ' ' ' Inputs:None ' ' Returns:None ' 'Assumes:Fancy Stuph using lots of Windo ' w API calls ' 'Side Effects:None known, unless your ap ' p hangs 'This code is copyrighted and has limite ' d warranties. 'Please see http://www.Planet-Source-Cod ' e.com/xq/ASP/txtCodeId.6362/lngWId.1/qx/ ' vb/scripts/ShowCode.htm 'for details. '************************************** ' ' Sorry I haven't put the declarations i ' n it's box and all that jazz, but I did ' it like this ' So that you could select it all and ju ' st place it into a new form. ' Yes, it's that easy. Create a new form ' , copy and paste this code. ' Then click on the form and hold down t ' he mouse, and drag over to another windo ' w. ' ' Jolyon Bloomfield, February 2000 ' ' A note to using this code: I guess sin ' ce I've put it here, anybody can use it. ' ' If you do, please give me credit for t ' he hard work that I put into this. ' It wasn't an easy process, and I don't ' want anybody taking credit for my work. ' ' ' The only bug I've found, is that when ' a window is maximised, it has coordinate ' s ' that exceed the bounding area of the s ' creen. I tried to offset this effect, ' but gave up. ' Option Explicit' Require variable Declaration ' PointAPI and RECT are the two most com ' mon structures used in graphics in Windo ' ws Private Type POINTAPI X As Long Y As Long End Type Private Type RECT Left As Long Top As Long Right As Long Bottom As Long End Type Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long' Get the cursor position Private Declare Function WindowFromPoint Lib "user32" (ByVal xPoint As Long, ByVal yPoint As Long) As Long' Get the handle of the window that is foremost on a particular X, Y position. Used here to get the window under the cursor Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long ' Get the window co-ordinates in a RECT structure Private Declare Function GetWindowDC Lib "user32" (ByVal hwnd As Long) As Long ' Retrieve a handle for the hDC of a window Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hdc As Long) As Long ' Release the memory occupied by an hDC Private Declare Function CreatePen Lib "gdi32" (ByVal nPenStyle As Long, ByVal nWidth As Long, ByVal crColor As Long) As Long' Create a GDI graphics pen object Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long' Used to select brushes, pens, and clipping regions Private Declare Function GetStockObject Lib "gdi32" (ByVal nIndex As Long) As Long ' Get hold of a "stock" object. I use it to get a Null Brush Private Declare Function SetROP2 Lib "gdi32" (ByVal hdc As Long, ByVal nDrawMode As Long) As Long' Used to set the Raster OPeration of a window Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long' Delete a GDI Object Private Declare Function Rectangle Lib "gdi32" (ByVal hdc As Long, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long' GDI Graphics- draw a rectangle using current pen, brush, etc. Private Declare Function SetCapture Lib "user32" (ByVal hwnd As Long) As Long ' Set mouse events only for one window Private Declare Function ReleaseCapture Lib "user32" () As Long' Release the mouse capture Private Declare Function CreateRectRgn Lib "gdi32" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long ' Create a rectangular region Private Declare Function SelectClipRgn Lib "gdi32" (ByVal hdc As Long, ByVal hRgn As Long) As Long ' Select the clipping region of an hDC Private Declare Function GetClipRgn Lib "gdi32" (ByVal hdc As Long, ByVal hRgn As Long) As Long' Get the Clipping region of an hDC Private Const NULL_BRUSH = 5' Stock Object Private Selecting As Boolean ' Amd I currently selecting a window? Private BorderDrawn As Boolean' Is there a border currently drawn that needs to be undrawn? Private Myhwnd As Long ' The current hWnd that has a border drawn on it Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) ' Set the selecting flag Selecting = True ' Capture all mouse events to this windo ' w (form) SetCapture Me.hwnd ' Simulate a mouse movement event to dra ' w the border when the mouse button goes ' down Form_MouseMove 0, Shift, X, Y End Sub Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single) ' Security catch to make sure that the g ' raphics don't get mucked up when not sel ' ecting If Selecting = False Then Exit Sub ' Call the "Draw" subroutine Draw End Sub Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single) ' If not selecting, then skip If Selecting = False Then Exit Sub ' Clean up the graphics drawn UnDraw ' Release mouse capture ReleaseCapture ' Not selecting Selecting = False ' Reset the variable Myhwnd = 0 End Sub Private Sub Draw() Dim Cursor As POINTAPI' Cursor position Dim RetVal As Long' Dummy returnvalue Dim hdc As Long' hDC that we're going to be using Dim Pen As Long' Handle to a GDI Pen object Dim Brush As Long ' Handle to a GDI Brush object Dim OldPen As Long' Handle to previous Pen object (to restore it) Dim OldBrush As Long ' Handle to previous brush object (to restore it) Dim OldROP As Long' Value of the previous ROP Dim Region As Long' Handle to a GDI Region object that I create Dim OldRegion As Long ' Handle to previous Region object for the hDC Dim FullWind As RECT ' the bounding rectangle of the window in screen coords Dim Draw As RECT ' The drawing rectangle ' ' Getting all of the ingredients ready ' ' Get the cursor GetCursorPos Cursor ' Get the window RetVal = WindowFromPoint(Cursor.X, Cursor.Y) ' If the new hWnd is the same as the old ' one, skip drawing it, so to avoid flicke ' r If RetVal = Myhwnd Then Exit Sub ' New hWnd. If there is currently a bord ' er drawn, undraw it. If BorderDrawn = True Then UnDraw ' Set the BorderDrawn property to true, ' as we're just about to draw it. BorderDrawn = True ' And set the hWnd to the new value. ' Note, I didn't do it before, because t ' he UnDraw routine uses the Myhwnd variab ' le Myhwnd = RetVal ' Print the hWnd on the form in Hex (jus ' t so see what windows are at work) Me.Cls Me.Print Hex(Myhwnd) ' You could extract other information fr ' om the window, such as window title, ' class name, parent, etc., and print it ' here, too. ' Get the full Rect of the window in scr ' een co-ords GetWindowRect Myhwnd, FullWind ' Create a region with width and height ' of the window Region = CreateRectRgn(0, 0, FullWind.Right - FullWind.Left, FullWind.Bottom - FullWind.Top) ' Create an hDC for the hWnd ' Note: GetDC retrieves the CLIENT AREA ' hDC. We want the WHOLE WINDOW, including ' Non-Client ' stuff like title bar, menu, border, et ' c. hdc = GetWindowDC(Myhwnd) ' Save the old region RetVal = GetClipRgn(hdc, OldRegion) ' Retval = 0: no region 1: Region copied ' -1: error ' Select the new region RetVal = SelectObject(hdc, Region) ' Create a pen Pen = CreatePen(DrawStyleConstants.vbSolid, 6, 0)' Draw Solid lines, width 6, and color black ' Select the pen ' A pen draws the lines OldPen = SelectObject(hdc, Pen) ' Create a brush ' A brush is the filling for a shape ' I need to set it to a null brush so th ' at it doesn't edit anything Brush = GetStockObject(NULL_BRUSH) ' Select the brush OldBrush = SelectObject(hdc, Brush) ' Select the ROP OldROP = SetROP2(hdc, DrawModeConstants.vbInvert)' vbInvert means, whatever is draw, ' invert those pixels. This means that I ' can undraw it by doing the same. ' ' The Drawing Bits ' ' Put a box around the outside of the wi ' ndow, using the current hDC. ' These coords are in device co-ordinate ' s, i.e., of the hDC. With Draw .Left = 0 .Top = 0 .Bottom = FullWind.Bottom - FullWind.Top .Right = FullWind.Right - FullWind.Left Rectangle hdc, .Left, .Top, .Right, .Bottom' Really easy to understand - draw a rectangle, hDC, and coordinates End With ' ' The Washing Up bits ' ' This is a very important part, as it r ' eleases memory that has been taken up. ' If we don't do this, windows crashes d ' ue to a memory leak. ' You probably get a blue screen (altohu ' gh I'm not sure) ' ' Get back the old region SelectObject hdc, OldRegion ' Return the previous ROP SetROP2 hdc, OldROP ' Return to the previous brush SelectObject hdc, OldBrush ' Return the previous pen SelectObject hdc, OldPen ' Delete the Brush I created DeleteObject Brush ' Delete the Pen I created DeleteObject Pen ' Delete the region I created DeleteObject Region ' Release the hDC back to window's resou ' rce pool ReleaseDC Myhwnd, hdc End Sub Private Sub UnDraw() ' ' Note, this sub is almost identical to ' the other one, except it doesn't go look ' ing ' for the hWnd, it accesses the old one. ' Also, it doesn't clear the form. ' Otherwise, it just draws on top of the ' old one with an invert pen. ' 2 inverts = original ' ' If there hasn't been a border drawn, t ' hen get out of here. If BorderDrawn = False Then Exit Sub ' Now set it BorderDrawn = False ' If there isn't a current hWnd, then ex ' it. ' That's why in the mouseup event we get ' out, because otherwise a border would be ' draw ' around the old window If Myhwnd = 0 Then Exit Sub Dim Cursor As POINTAPI' Cursor position Dim RetVal As Long' Dummy returnvalue Dim hdc As Long' hDC that we're going to be using Dim Pen As Long' Handle to a GDI Pen object Dim Brush As Long ' Handle to a GDI Brush object Dim OldPen As Long' Handle to previous Pen object (to restore it) Dim OldBrush As Long ' Handle to previous brush object (to restore it) Dim OldROP As Long' Value of the previous ROP Dim Region As Long' Handle to a GDI Region object that I create Dim OldRegion As Long ' Handle to previous Region object for the hDC Dim FullWind As RECT ' the bounding rectangle of the window in screen coords Dim Draw As RECT ' The drawing rectangle ' ' Getting all of the ingredients ready ' ' Get the full Rect of the window in scr ' een co-ords GetWindowRect Myhwnd, FullWind ' Create a region with width and height ' of the window Region = CreateRectRgn(0, 0, FullWind.Right - FullWind.Left, FullWind.Bottom - FullWind.Top) ' Create an hDC for the hWnd ' Note: GetDC retrieves the CLIENT AREA ' hDC. We want the WHOLE WINDOW, including ' Non-Client ' stuff like title bar, menu, border, et ' c. hdc = GetWindowDC(Myhwnd) ' Save the old region RetVal = GetClipRgn(hdc, OldRegion) ' Retval = 0: no region 1: Region copied ' -1: error ' Select the new region RetVal = SelectObject(hdc, Region) ' Create a pen Pen = CreatePen(DrawStyleConstants.vbSolid, 6, 0)' Draw Solid lines, width 6, and color black ' Select the pen ' A pen draws the lines OldPen = SelectObject(hdc, Pen) ' Create a brush ' A brush is the filling for a shape ' I need to set it to a null brush so th ' at it doesn't edit anything Brush = GetStockObject(NULL_BRUSH) ' Select the brush OldBrush = SelectObject(hdc, Brush) ' Select the ROP OldROP = SetROP2(hdc, DrawModeConstants.vbInvert)' vbInvert means, whatever is draw, ' invert those pixels. This means that I ' can undraw it by doing the same. ' ' The Drawing Bits ' ' Put a box around the outside of the wi ' ndow, using the current hDC. ' These coords are in device co-ordinate ' s, i.e., of the hDC. With Draw .Left = 0 .Top = 0 .Bottom = FullWind.Bottom - FullWind.Top .Right = FullWind.Right - FullWind.Left Rectangle hdc, .Left, .Top, .Right, .Bottom' Really easy to understand - draw a rectangle, hDC, and coordinates End With ' ' The Washing Up bits ' ' This is a very important part, as it r ' eleases memory that has been taken up. ' If we don't do this, windows crashes d ' ue to a memory leak. ' You probably get a blue screen (altohu ' gh I'm not sure) ' ' Get back the old region SelectObject hdc, OldRegion ' Return the previous ROP SetROP2 hdc, OldROP ' Return to the previous brush SelectObject hdc, OldBrush ' Return the previous pen SelectObject hdc, OldPen ' Delete the Brush I created DeleteObject Brush ' Delete the Pen I created DeleteObject Pen ' Delete the region I created DeleteObject Region ' Release the hDC back to window's resou ' rce pool ReleaseDC Myhwnd, hdc End Sub