Click here to Skip to main content
15,881,882 members
Articles / Desktop Programming / MFC

MS Spy++ style Window Finder

Rate me:
Please Sign up or sign in to vote.
4.84/5 (43 votes)
9 Jul 2002CPOL11 min read 457.2K   26.5K   211  
Ever wondered how the cool Microsoft Spy++ Window Finder Tool is created ? Here is one possible implementation.



#include "WindowFinder.h"
#include "main.h"
#include "resource.h"

#define BULLSEYE_CENTER_X_OFFSET		15
#define BULLSEYE_CENTER_Y_OFFSET		18





// Synopsis :
// 1. This routine launches the "Search Window" dialog box.
//
// 2. The dialog box is a MODAL dialog box that will not return until the user 
// clicks on the "OK" or "Cancel" button.
long StartSearchWindowDialog (HWND hwndMain)
{
  long lRet = 0;

  lRet = (long)DialogBox
  (
    (HINSTANCE)g_hInst, // handle to application instance 
    (LPCTSTR)MAKEINTRESOURCE(IDD_DIALOG_SEARCH_WINDOW), // identifies dialog box template 
    (HWND)hwndMain, // handle to owner window 
    (DLGPROC)SearchWindowDialogProc // pointer to dialog box procedure 
  );

  return lRet;
}





// Synopsis :
// 1. This function checks a hwnd to see if it is actually the "Search Window" Dialog's or Main Window's
// own window or one of their children. If so a FALSE will be returned so that these windows will not
// be selected. 
//
// 2. Also, this routine checks to see if the hwnd to be checked is already a currently found window.
// If so, a FALSE will also be returned to avoid repetitions.
BOOL CheckWindowValidity (HWND hwndDialog, HWND hwndToCheck)
{
  HWND hwndTemp = NULL;
  BOOL bRet = TRUE;

  // The window must not be NULL.
  if (hwndToCheck == NULL)
  {
    bRet = FALSE;
	goto CheckWindowValidity_0;
  }

  // It must also be a valid window as far as the OS is concerned.
  if (IsWindow(hwndToCheck) == FALSE)
  {
    bRet = FALSE;
	goto CheckWindowValidity_0;
  }

  // Ensure that the window is not the current one which has already been found.
  if (hwndToCheck == g_hwndFoundWindow)
  {
    bRet = FALSE;
	goto CheckWindowValidity_0;
  }

  // It must also not be the main window itself.
  if (hwndToCheck == g_hwndMainWnd)
  {
    bRet = FALSE;
	goto CheckWindowValidity_0;
  }
  
  // It also must not be the "Search Window" dialog box itself.
  if (hwndToCheck == hwndDialog)
  {
    bRet = FALSE;
	goto CheckWindowValidity_0;
  }

  // It also must not be one of the dialog box's children...
  hwndTemp = GetParent (hwndToCheck);
  if ((hwndTemp == hwndDialog) || (hwndTemp == g_hwndMainWnd))
  {
    bRet = FALSE;
	goto CheckWindowValidity_0;
  }

CheckWindowValidity_0:

  return bRet;
}





// Synopsis :
// 1. This is the handler for WM_MOUSEMOVE messages sent to the "Search Window" dialog proc.
//
// 2. Note that we do not handle every WM_MOUSEMOVE message sent. Instead, we check to see 
// if "g_bStartSearchWindow" is TRUE. This BOOL will be set to TRUE when the Window
// Searching Operation is actually started. See the WM_COMMAND message handler in 
// SearchWindowDialogProc() for more details.
//
// 3. Because the "Search Window" dialog immediately captures the mouse when the Search Operation 
// is started, all mouse movement is monitored by the "Search Window" dialog box. This is 
// regardless of whether the mouse is within or without the "Search Window" dialog. 
//
// 4. One important note is that the horizontal and vertical positions of the mouse cannot be 
// calculated from "lParam". These values can be inaccurate when the mouse is outside the
// dialog box. Instead, use the GetCursorPos() API to capture the position of the mouse.
long DoMouseMove 
(
  HWND hwndDialog, 
  UINT message, 
  WPARAM wParam, 
  LPARAM lParam
)
{
  POINT		screenpoint;
  HWND		hwndFoundWindow = NULL;
  char		szText[256];
  long		lRet = 0;

  // Must use GetCursorPos() instead of calculating from "lParam".
  GetCursorPos (&screenpoint);  

  // Display global positioning in the dialog box.
  wsprintf (szText, "%d", screenpoint.x);
  SetDlgItemText (hwndDialog, IDC_STATIC_X_POS, szText);
  
  wsprintf (szText, "%d", screenpoint.y);
  SetDlgItemText (hwndDialog, IDC_STATIC_Y_POS, szText);

  // Determine the window that lies underneath the mouse cursor.
  hwndFoundWindow = WindowFromPoint (screenpoint);

  // Check first for validity.
  if (CheckWindowValidity (hwndDialog, hwndFoundWindow))
  {
    // We have just found a new window.

    // Display some information on this found window.
	DisplayInfoOnFoundWindow (hwndDialog, hwndFoundWindow);

    // If there was a previously found window, we must instruct it to refresh itself. 
	// This is done to remove any highlighting effects drawn by us.
    if (g_hwndFoundWindow)
    {
      RefreshWindow (g_hwndFoundWindow);
    }

    // Indicate that this found window is now the current global found window.
    g_hwndFoundWindow = hwndFoundWindow;

    // We now highlight the found window.
    HighlightFoundWindow (hwndDialog, g_hwndFoundWindow);
  }

  return lRet;
}





// Synopsis :
// 1. Handler for WM_LBUTTONUP message sent to the "Search Window" dialog box.
// 
// 2. We restore the screen cursor to the previous one.
//
// 3. We stop the window search operation and release the mouse capture.
long DoMouseUp
(
  HWND hwndDialog, 
  UINT message, 
  WPARAM wParam, 
  LPARAM lParam
)
{
  long lRet = 0;

  // If we had a previous cursor, set the screen cursor to the previous one.
  // The cursor is to stay exactly where it is currently located when the 
  // left mouse button is lifted.
  if (g_hCursorPrevious)
  {
    SetCursor (g_hCursorPrevious);
  }

  // If there was a found window, refresh it so that its highlighting is erased. 
  if (g_hwndFoundWindow)
  {
    RefreshWindow (g_hwndFoundWindow);
  }

  // Set the bitmap on the Finder Tool icon to be the bitmap with the bullseye bitmap.
  SetFinderToolImage (hwndDialog, TRUE);

  // Very important : must release the mouse capture.
  ReleaseCapture ();

  // Make the main window appear normally.
  ShowWindow (g_hwndMainWnd, SW_SHOWNORMAL);

  // Set the global search window flag to FALSE.
  g_bStartSearchWindow = FALSE;

  return lRet;
}





// Synopsis :
// 1. This routine sets the Finder Tool icon to contain an appropriate bitmap.
//
// 2. If bSet is TRUE, we display the BullsEye bitmap. Otherwise the empty window
// bitmap is displayed.
BOOL SetFinderToolImage (HWND hwndDialog, BOOL bSet)
{
  HBITMAP hBmpToSet = NULL;
  BOOL bRet = TRUE;

  if (bSet)
  {
    // Set a FILLED image.
	hBmpToSet = g_hBitmapFinderToolFilled;
  }
  else
  {
    // Set an EMPTY image.
	hBmpToSet = g_hBitmapFinderToolEmpty;
  }

  SendDlgItemMessage
  (
    (HWND)hwndDialog, // handle of dialog box 
    (int)IDC_STATIC_ICON_FINDER_TOOL, // identifier of control 
    (UINT)STM_SETIMAGE, // message to send 
    (WPARAM)IMAGE_BITMAP, // first message parameter 
    (LPARAM)hBmpToSet // second message parameter 
  );

  return bRet;
}





// Synopsis :
// 1. This routine moves the mouse cursor hotspot to the exact 
// centre position of the bullseye in the finder tool static control.
//
// 2. This function, when used together with DoSetFinderToolImage(),
// gives the illusion that the bullseye image has indeed been transformed
// into a cursor and can be moved away from the Finder Tool Static
// control.
BOOL MoveCursorPositionToBullsEye (HWND hwndDialog)
{
  BOOL bRet = FALSE;
  HWND hwndToolFinder = NULL;
  RECT rect;
  POINT screenpoint;

  // Get the window handle of the Finder Tool static control.
  hwndToolFinder = GetDlgItem (hwndDialog, IDC_STATIC_ICON_FINDER_TOOL);

  if (hwndToolFinder)
  {
    // Get the screen coordinates of the static control,
	// add the appropriate pixel offsets to the center of 
	// the bullseye and move the mouse cursor to this exact
	// position.
    GetWindowRect (hwndToolFinder, &rect);
	screenpoint.x = rect.left + BULLSEYE_CENTER_X_OFFSET;
    screenpoint.y = rect.top + BULLSEYE_CENTER_Y_OFFSET;
	SetCursorPos (screenpoint.x, screenpoint.y);
  }

  return bRet;
}





// Synopsis :
// 1. This function starts the window searching operation.
//
// 2. A very important part of this function is to capture 
// all mouse activities from now onwards and direct all mouse 
// messages to the "Search Window" dialog box procedure.
long SearchWindow (HWND hwndDialog)
{
  long lRet = 0;

  // Set the global "g_bStartSearchWindow" flag to TRUE.
  g_bStartSearchWindow = TRUE;

  // Display the empty window bitmap image in the Finder Tool static control.
  SetFinderToolImage (hwndDialog, FALSE);

  MoveCursorPositionToBullsEye (hwndDialog);

  // Set the screen cursor to the BullsEye cursor.
  if (g_hCursorSearchWindow)
  {
    g_hCursorPrevious = SetCursor (g_hCursorSearchWindow);
  }
  else
  {
    g_hCursorPrevious = NULL;
  }

  // Very important : capture all mouse activities from now onwards and
  // direct all mouse messages to the "Search Window" dialog box procedure.
  SetCapture (hwndDialog);

  // Hide the main window.
  ShowWindow (g_hwndMainWnd, SW_HIDE);
  
  return lRet;
}





long DisplayInfoOnFoundWindow (HWND hwndDialog, HWND hwndFoundWindow)
{
  RECT		rect;              // Rectangle area of the found window.
  char		szText[256];
  char		szClassName[100];
  long		lRet = 0;

  // Get the screen coordinates of the rectangle of the found window.
  GetWindowRect (hwndFoundWindow, &rect);

  // Get the class name of the found window.
  GetClassName (hwndFoundWindow, szClassName, sizeof (szClassName) - 1);

  // Display some information on the found window.
  wsprintf
  (
    szText, "Window Handle == 0x%08X.\r\nClass Name : %s.\r\nRECT.left == %d.\r\nRECT.top == %d.\r\nRECT.right == %d.\r\nRECT.bottom == %d.\r\n",
    hwndFoundWindow,
	szClassName, 
    rect.left,
    rect.top,
    rect.right,
    rect.bottom
  );

  SetDlgItemText (hwndDialog, IDC_EDIT_STATUS, szText);

  return lRet;
}





long RefreshWindow (HWND hwndWindowToBeRefreshed)
{
  long lRet = 0;

  InvalidateRect (hwndWindowToBeRefreshed, NULL, TRUE);
  UpdateWindow (hwndWindowToBeRefreshed);
  RedrawWindow (hwndWindowToBeRefreshed, NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);

  return lRet;
}





// Performs a highlighting of a found window.
// Comments below will demonstrate how this is done.
long HighlightFoundWindow (HWND hwndDialog, HWND hwndFoundWindow)
{
  HDC		hWindowDC = NULL;  // The DC of the found window.
  HGDIOBJ	hPrevPen = NULL;   // Handle of the existing pen in the DC of the found window.
  HGDIOBJ	hPrevBrush = NULL; // Handle of the existing brush in the DC of the found window.
  RECT		rect;              // Rectangle area of the found window.
  long		lRet = 0;

  // Get the screen coordinates of the rectangle of the found window.
  GetWindowRect (hwndFoundWindow, &rect);

  // Get the window DC of the found window.
  hWindowDC = GetWindowDC (hwndFoundWindow);

  if (hWindowDC)
  {
    // Select our created pen into the DC and backup the previous pen.
    hPrevPen = SelectObject (hWindowDC, g_hRectanglePen);

    // Select a transparent brush into the DC and backup the previous brush.
    hPrevBrush = SelectObject (hWindowDC, GetStockObject(HOLLOW_BRUSH));

    // Draw a rectangle in the DC covering the entire window area of the found window.
    Rectangle (hWindowDC, 0, 0, rect.right - rect.left, rect.bottom - rect.top);

    // Reinsert the previous pen and brush into the found window's DC.
    SelectObject (hWindowDC, hPrevPen);

    SelectObject (hWindowDC, hPrevBrush);

    // Finally release the DC.
    ReleaseDC (hwndFoundWindow, hWindowDC);
  }

  return lRet;
}





BOOL CALLBACK SearchWindowDialogProc
(
  HWND hwndDlg, // handle to dialog box 
  UINT uMsg, // message 
  WPARAM wParam, // first message parameter 
  LPARAM lParam // second message parameter 
)
{
  BOOL bRet = FALSE;  // Default return value.

  switch (uMsg)
  {
    case WM_INITDIALOG :
	{
	  bRet = TRUE;
	  break;
	}

	case WM_MOUSEMOVE :
	{
      bRet = TRUE;

	  if (g_bStartSearchWindow)
	  {
	    // Only when we have started the Window Searching operation will we 
		// track mouse movement.
	    DoMouseMove(hwndDlg, uMsg, wParam, lParam);
	  }

	  break;
	}

	case WM_LBUTTONUP :
	{
      bRet = TRUE;

	  if (g_bStartSearchWindow)
	  {
	    // Only when we have started the window searching operation will we
		// be interested when the user lifts up the left mouse button.
	    DoMouseUp(hwndDlg, uMsg, wParam, lParam);
	  }

	  break;
	}

	case WM_COMMAND :
	{
      WORD wNotifyCode = HIWORD(wParam); // notification code 
      WORD wID = LOWORD(wParam);         // item, control, or accelerator identifier 
      HWND hwndCtl = (HWND)lParam;      // handle of control 

	  if ((wID == IDOK) || (wID == IDCANCEL))
	  {
	    bRet = TRUE;
	    EndDialog (hwndDlg, wID);
	  }

	  if (wID == IDC_STATIC_ICON_FINDER_TOOL)
	  {
	    // Because the IDC_STATIC_ICON_FINDER_TOOL static control is set with the SS_NOTIFY
		// flag, the Search Window's dialog box will be sent a WM_COMMAND message when this 
		// static control is clicked.
        bRet = TRUE;
		// We start the window search operation by calling the DoSearchWindow() function.
		SearchWindow(hwndDlg);
	    break;
	  }

	  break;
	}

	default :
	{
	  bRet = FALSE;
	  break;
	}
  }

  return bRet;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Systems Engineer NEC
Singapore Singapore
Lim Bio Liong is a Specialist at a leading Software House in Singapore.

Bio has been in software development for over 10 years. He specialises in C/C++ programming and Windows software development.

Bio has also done device-driver development and enjoys low-level programming. Bio has recently picked up C# programming and has been researching in this area.

Comments and Discussions