home *** CD-ROM | disk | FTP | other *** search
/ Windows Shareware GOLD / NuclearComputingVol3No1.cdr / other / f1398 / spy.c < prev    next >
C/C++ Source or Header  |  1991-04-28  |  36KB  |  871 lines

  1. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *\
  2.  *  Spy.c                                                                    *
  3.  *  Windows Spy Program                                                      *
  4.  *  Public Domain                                                            *
  5.  *  Written by Michael Geary                                                 *
  6.  *                                                                           *
  7.  *  This program "spies" on all the windows that are currently open in your  *
  8.  *  Windows session, and displays a window containing all the information it *
  9.  *  can find out about those windows.  You can scroll through this window    *
  10.  *  using either the mouse or keyboard to view the information about the     *
  11.  *  various windows.  The "New Spy Mission" menu item re-captures the latest *
  12.  *  information.  This menu item is on the System menu so you can trigger it *
  13.  *  even if the Spy window is iconic.                                        *
  14. \* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  15.  
  16. #define LINT_ARGS
  17. #include <stdio.h>
  18. #include <stdarg.h>
  19. #include <windows.h>
  20. #include "spy.h"
  21.  
  22. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  23.  
  24. /*  The display for a single window looks like this in collapsed mode:
  25.  *
  26.  *  {Child|Popup|TopLevel} window HHHH {class} (L,T;R,B) "title"
  27.  *
  28.  *  or like this in expanded mode:
  29.  *
  30.  *      {Child|Popup|TopLevel} window handle: HHHH
  31.  *        Class name: {class name}
  32.  *        Window title: {title text}
  33.  *        Parent window handle: HHHH
  34.  *        Class function, window function: HHHH:HHHH, HHHH:HHHH
  35.  *        Class module handle, Window instance handle: HHHH, HHHH
  36.  *        Class extra alloc, Window extra alloc: DDDDD, DDDDD
  37.  *        Class style, Window style: HHHH, HHHHHHHH
  38.  *        Menu handle: HHHH   -or-  Control ID: DDDDD
  39.  *        Brush, Cursor, Icon handles: HHHH, HHHH, HHHH
  40.  *        Window rectangle: Left=DDDDD, Top=DDDDD, Right=DDDDD, Bottom=DDDDD
  41.  *        Client rectangle: Left=DDDDD, Top=DDDDD, Right=DDDDD, Bottom=DDDDD
  42.  *      {blank line}
  43.  *
  44.  *  Total number of lines for one window display: 13
  45.  */
  46.  
  47. #define LINES_PER_WINDOW    13
  48. #define WINDOW_WIDTH        120
  49.  
  50. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  51.  
  52. /*  The INFO structure contains all the information we gather up about each
  53.  *  window we are spying on.  We allocate an array of INFO structures in the
  54.  *  global heap, with one entry for each window in the system.
  55.  */
  56.  
  57. #define CLASSMAX    30
  58. #define TITLEMAX    50
  59.  
  60. typedef struct {
  61.     HWND    winHWnd;                /* Window handle */
  62.     char    winClass[CLASSMAX];     /* Class name */
  63.     HBRUSH  winBkgdBrush;           /* Background brush handle */
  64.     HCURSOR winCursor;              /* Cursor handle */
  65.     HICON   winIcon;                /* Icon handle */
  66.     HANDLE  winClassModule;         /* Module handle for owner of class */
  67.     WORD    winWndExtra;            /* Extra data allocated for each window */
  68.     WORD    winClsExtra;            /* Extra data allocated in class itself */
  69.     WORD    winClassStyle;          /* Class style word */
  70.     FARPROC winClassProc;           /* Window function declared for class */
  71.     HANDLE  winInstance;            /* Instance handle for window owner */
  72.     HWND    winHWndParent;          /* Parent window handle */
  73.     char    winTitle[TITLEMAX];     /* Window title or content string */
  74.     WORD    winControlID;           /* Control ID or menu handle */
  75.     FARPROC winWndProc;             /* Window function, usually = class fun. */
  76.     DWORD   winStyle;               /* Style doubleword for window (WS_...) */
  77.     RECT    winWindowRect;          /* Window rectangle (screen-relative) */
  78.     RECT    winClientRect;          /* Client rectangle within window rect. */
  79. } INFO;
  80.  
  81. typedef HANDLE      HINFO;          /* Handle to array of INFO structures */
  82. typedef INFO huge * LPINFO;         /* Far pointer to same when locked down */
  83.  
  84. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  85.  
  86. /*  The CsrScroll array is used for implementing keyboard scrolling.  By
  87.  *  looking up the keystroke in this array, we get the equivalent scroll
  88.  *  bar message.
  89.  */
  90.  
  91. #define VK_MIN_CURSOR  VK_PRIOR
  92. #define VK_MAX_CURSOR  VK_DOWN
  93.  
  94. struct {
  95.     char    csBar;      /* Which scroll bar this key is equivalent to */
  96.     char    csMsg;      /* The scroll message for this key */
  97. } CsrScroll[] = {
  98.     { SB_VERT, SB_PAGEUP   },  /* VK_PRIOR (PgUp)        */
  99.     { SB_VERT, SB_PAGEDOWN },  /* VK_NEXT  (PgDn)        */
  100.     { SB_VERT, SB_BOTTOM   },  /* VK_END   (End)         */
  101.     { SB_VERT, SB_TOP      },  /* VK_HOME  (Home)        */
  102.     { SB_HORZ, SB_LINEUP   },  /* VK_LEFT  (left arrow)  */
  103.     { SB_VERT, SB_LINEUP   },  /* VK_UP    (up arrow)    */
  104.     { SB_HORZ, SB_LINEDOWN },  /* VK_RIGHT (right arrow) */
  105.     { SB_VERT, SB_LINEDOWN }   /* VK_DOWN  (down arrow)  */
  106. };
  107.  
  108. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  109.  
  110. /*  Static variables
  111.  */
  112.  
  113. HANDLE      hInstance;              /* Our instance handle */
  114. HINFO       hInfo;                  /* Global handle to INFO array structure */
  115. LPINFO      lpInfo;                 /* Far pointer to INFO, when locked down */
  116. int         nWindows;               /* Total number of windows in system */
  117. DWORD       dwInfoSize;             /* Size of entire INFO array in bytes */
  118. FARPROC     lpprocCountWindow;      /* ProcInstance for CountWindow */
  119. FARPROC     lpprocSpyOnWindow;      /* ProcInstance for SpyOnWindow */
  120. BOOL        bInitted = FALSE;       /* TRUE when initialization completed */
  121. BOOL        bExpand = FALSE;        /* Expanded display mode? */
  122. int         nLinesPerWindow = 1;    /* 1 or LINES_PER_WINDOW */
  123. int         nCharSizeX;             /* Width of a character in pixels */
  124. int         nCharSizeY;             /* Height of a character in pixels */
  125. int         nExtLeading;            /* # pixels vertical space between chars */
  126. int         nPaintX;                /* For Paint function: X coordinate */
  127. int         nPaintY;                /* For Paint function: Y coordinate */
  128. HDC         hdcPaint;               /* For Paint function: hDC to paint into */
  129. char        szClass[10];            /* Our window class name */
  130. char        szTitle[40];            /* Our window title */
  131.  
  132. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  133.  
  134. /*  Declare full templates for all our functions.  This gives us strong type
  135.  *  checking on our functions.
  136.  */
  137.  
  138. BOOL    FAR PASCAL  CountWindow( HWND, long );
  139. int         PASCAL  DoScrollMsg( HWND, int, WORD, int );
  140. void        PASCAL  HomeScrollBars( HWND, BOOL );
  141. BOOL        PASCAL  Initialize( HANDLE, int );
  142. void        cdecl   Paint( char *, ... );
  143. void        PASCAL  PaintWindow( HWND );
  144. void        PASCAL  SetScrollBars( HWND );
  145. void        PASCAL  SetScrollBar1( HWND, int, int );
  146. BOOL        PASCAL  SpyOnAllWindows( HWND );
  147. BOOL    FAR PASCAL  SpyOnWindow( HWND, long );
  148. long    FAR PASCAL  SpyWndProc( HWND, unsigned, WORD, LONG );
  149. int         PASCAL  WinMain( HANDLE, HANDLE, LPSTR, int );
  150.  
  151. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  152.  
  153. /*  Enumeration function to count the number of windows in the system.  Called
  154.  *  once for each window, via EnumWindows and recursively via EnumChildWindows.
  155.  *  The lTopLevel parameter tells us which kind of call it is.
  156.  */
  157.  
  158. BOOL FAR PASCAL CountWindow( hWnd, lTopLevel )
  159.     HWND        hWnd;               /* Window handle for this window */
  160.     long        lTopLevel;          /* 1=top level window, 0=child window */
  161. {
  162.     /* Count the window */
  163.     dwInfoSize += sizeof(INFO);
  164.     ++nWindows;
  165.  
  166.     /* If this is a top level window (or popup), count its children */
  167.     if( lTopLevel )
  168.         EnumChildWindows( hWnd, lpprocCountWindow, 0L );
  169.  
  170.     return TRUE;  /* TRUE to continue enumeration */
  171. }
  172.  
  173. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  174.  
  175. /*  Process a scroll bar message.  Calculates the distance to scroll based on
  176.  *  the scroll bar range and the message code.  Limits the scroll to the actual
  177.  *  range of the scroll bar.  Sets the new scroll bar thumb position and
  178.  *  scrolls the window by the necessary amount.  Note that the scroll bar
  179.  *  ranges are set in terms of number of characters, while the window scrolling
  180.  *  is done by a number of pixels.  Returns the distance scrolled in chars.
  181.  */
  182.  
  183. int PASCAL DoScrollMsg( hWnd, nBar, wCode, nThumb )
  184.     HWND        hWnd;               /* Window handle to scroll */
  185.     int         nBar;               /* Which scroll bar: SB_HORZ or SB_VERT */
  186.     WORD        wCode;              /* The scroll bar message code */
  187.     int         nThumb;             /* Thumb position for SB_THUMBPOSITION */
  188. {
  189.     int         nOld;               /* Previous scroll bar position */
  190.     int         nDiff;              /* Amount to change scroll bar by */
  191.     int         nMin;               /* Minimum value of scroll bar range */
  192.     int         nMax;               /* Maximum value of scroll bar range */
  193.     int         nPageSize;          /* Size of our window in characters */
  194.     RECT        rect;               /* Client rectangle for our window */
  195.  
  196.     /* Get old scroll position and scroll range */
  197.     nOld = GetScrollPos( hWnd, nBar );
  198.     GetScrollRange( hWnd, nBar, &nMin, &nMax );
  199.  
  200.     /* Quit if there is nowhere to scroll to (see SetScrollBars) */
  201.     if( nMax == MAXINT )
  202.         return 0;
  203.  
  204.     /* Calculate page size, horizontal or vertical as needed */
  205.     GetClientRect( hWnd, &rect );
  206.     if( nBar == SB_HORZ )
  207.         nPageSize = (rect.right - rect.left) / nCharSizeX;
  208.     else
  209.         nPageSize = (rect.bottom - rect.top) / nCharSizeY;
  210.  
  211.     /* Select the amount to scroll by, based on the scroll message */
  212.     switch( wCode ) {
  213.  
  214.         case SB_LINEUP:
  215.             nDiff = -1;
  216.             break;
  217.  
  218.         case SB_LINEDOWN:
  219.             nDiff = 1;
  220.             break;
  221.  
  222.         case SB_PAGEUP:
  223.             nDiff = -nPageSize;
  224.             break;
  225.  
  226.         case SB_PAGEDOWN:
  227.             nDiff = nPageSize;
  228.             break;
  229.  
  230.         case SB_THUMBPOSITION:
  231.             nDiff = nThumb - nOld;
  232.             break;
  233.  
  234.         case SB_TOP:
  235.             nDiff = -30000;  /* Kind of a kludge but it works... */
  236.             break;
  237.  
  238.         case SB_BOTTOM:
  239.             nDiff = 30000;
  240.             break;
  241.  
  242.         default:
  243.             return 0;
  244.     }
  245.  
  246.     /* Limit scroll destination to nMin..nMax */
  247.     if( nDiff < nMin - nOld )
  248.         nDiff = nMin - nOld;
  249.  
  250.     if( nDiff > nMax - nOld )
  251.         nDiff = nMax - nOld;
  252.  
  253.     if( nDiff == 0 )
  254.         return 0;  /* Return if net effect is nothing */
  255.  
  256.     /* OK, now we can set the new scroll bar position and scroll the window */
  257.     SetScrollPos( hWnd, nBar, nOld + nDiff, TRUE );
  258.  
  259.     ScrollWindow(
  260.         hWnd,
  261.         nBar == SB_HORZ ?  -nDiff*nCharSizeX : 0,
  262.         nBar == SB_HORZ ?  0 : -nDiff*nCharSizeY,
  263.         NULL,
  264.         NULL
  265.     );
  266.  
  267.     /* Force an immediate update for cleaner appearance */
  268.     UpdateWindow( hWnd );
  269.  
  270.     return nDiff;
  271. }
  272.  
  273. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  274.  
  275. /*  Set both scroll bars to the home position (0)
  276.  */
  277.  
  278. void PASCAL HomeScrollBars( hWnd, bRedraw )
  279.     HWND        hWnd;               /* Window handle */
  280.     BOOL        bRedraw;            /* TRUE if scroll bars should be redrawn */
  281. {
  282.     SetScrollPos( hWnd, SB_HORZ, 0, bRedraw );
  283.     SetScrollPos( hWnd, SB_VERT, 0, bRedraw );
  284. }
  285.  
  286. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  287.  
  288. /*  Initialize the application.  Some of the initialization is different
  289.  *  depending on whether this is the first instance or a subsequent instance.
  290.  *  For example, we register our window class only in the first instance.
  291.  *  Returns TRUE if initialization succeeded, FALSE if failed.
  292.  */
  293.  
  294. BOOL PASCAL Initialize( hPrevInst, nCmdShow )
  295.     HANDLE      hPrevInst;          /* Previous instance handle, 0 if first */
  296.     int         nCmdShow;           /* Parameter from WinMain for ShowWindow */
  297. {
  298.     WNDCLASS    Class;              /* Class structure for RegisterClass */
  299.     HWND        hWnd;               /* Our window handle */
  300.     HDC         hDC;                /* Display context for our window */
  301.     TEXTMETRIC  Metrics;            /* Text metrics for System font */
  302.     HMENU       hMenu;              /* Menu handle of system menu */
  303.     int         nScreenX;
  304.     int         nScreenY;
  305.  
  306.     nScreenX = GetSystemMetrics( SM_CXSCREEN );
  307.     nScreenY = GetSystemMetrics( SM_CYSCREEN );
  308.  
  309.     if( ! hPrevInst ) {
  310.         /* Initialization for first instance only */
  311.  
  312.         /* Load strings from resource file */
  313.         LoadString( hInstance, IDS_CLASS,    szClass,    sizeof(szClass) );
  314.         LoadString( hInstance, IDS_TITLE,    szTitle,    sizeof(szTitle) );
  315.  
  316.         /* Register our window class */
  317.         Class.style          = CS_HREDRAW | CS_VREDRAW;
  318.         Class.lpfnWndProc    = SpyWndProc;
  319.         Class.cbClsExtra     = 0;
  320.         Class.cbWndExtra     = 0;
  321.         Class.hInstance      = hInstance;
  322.         Class.hIcon          = LoadIcon( hInstance, szClass );
  323.         Class.hCursor        = LoadCursor( NULL, IDC_ARROW );
  324.         Class.hbrBackground  = COLOR_WINDOW + 1;
  325.         Class.lpszMenuName   = szClass;
  326.         Class.lpszClassName  = szClass;
  327.  
  328.         if( ! RegisterClass( &Class ) )
  329.             return FALSE;
  330.  
  331.     } else {
  332.         /* Initialization for subsequent instances only */
  333.  
  334.         /* Copy data from previous instance */
  335.         GetInstanceData( hPrevInst, szClass,    sizeof(szClass) );
  336.         GetInstanceData( hPrevInst, szTitle,    sizeof(szTitle) );
  337.     }
  338.  
  339.     /* Initialization for every instance */
  340.  
  341.     /* Set up ProcInstance pointers for our Enumerate functions */
  342.     lpprocCountWindow = MakeProcInstance( CountWindow, hInstance );
  343.     lpprocSpyOnWindow = MakeProcInstance( SpyOnWindow, hInstance );
  344.     if( ! lpprocCountWindow || ! lpprocSpyOnWindow )
  345.         return FALSE;
  346.  
  347.     /* Allocate our INFO structure with nothing really allocated yet */
  348.     hInfo = GlobalAlloc( GMEM_MOVEABLE, 1L );
  349.     if( ! hInfo )
  350.         return FALSE;
  351.  
  352.     /* Create our tiled window but don't display it yet */
  353.     hWnd = CreateWindow(
  354.         szClass,                    /* Class name */
  355.         szTitle,                    /* Window title */
  356.         WS_TILEDWINDOW | WS_HSCROLL | WS_VSCROLL,  /* Window style */
  357.         nScreenX *  1 / 20,         /* X: 5% from left */
  358.         nScreenY *  1 / 10,         /* Y  10% from top */
  359.         nScreenX *  9 / 10,         /* nWidth: 90% */
  360.         nScreenY *  7 / 10,         /* nHeight: 70% */
  361.         NULL,                       /* Parent hWnd (none for top-level) */
  362.         NULL,                       /* Menu handle */
  363.         hInstance,                  /* Owning instance handle */
  364.         NULL                        /* Parameter to pass in WM_CREATE (none) */
  365.     );
  366.  
  367.     /* Initialize scroll bars - Windows doesn't do this for us */
  368.     HomeScrollBars( hWnd, FALSE );
  369.  
  370.     /* Calculate character size for system font */
  371.     hDC = GetDC( hWnd );
  372.     GetTextMetrics( hDC, &Metrics );
  373.     ReleaseDC( hWnd, hDC );
  374.     nExtLeading = Metrics.tmExternalLeading;
  375.     nCharSizeX = Metrics.tmMaxCharWidth;
  376.     nCharSizeY = Metrics.tmHeight + Metrics.tmExternalLeading;
  377.  
  378.     /* Make the window visible before grabbing spy info, so it's included */
  379.     ShowWindow( hWnd, nCmdShow );
  380.  
  381.     /* Now grab the spy information */
  382.     if( ! SpyOnAllWindows( hWnd ) )
  383.         return FALSE;
  384.  
  385.     /* Got all the information, update our display */
  386.     UpdateWindow( hWnd );
  387.  
  388.     /* Make note that initialization is complete.  This is checked in our
  389.      * routine that handles WM_SIZE to eliminate some jitter on startup */
  390.     bInitted = TRUE;
  391.     return TRUE;
  392. }
  393.  
  394. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  395.  
  396. /*  Format and paint a line of text.  szFormat and Args are just as in a
  397.  *  sprintf() call (Args is a variable number of arguments).  The global
  398.  *  variables nPaintX and nPaintY tell where to paint the line.  We increment
  399.  *  nPaintY to the next line after painting.
  400.  *  Note the 'cdecl' declaration.  This forces this function to use the
  401.  *  standard C calling sequence, which is necessary with a variable number
  402.  *  of parameters.
  403.  */
  404.  
  405. void cdecl Paint( char *szFormat, ... )
  406. /*  char *      szFormat;  -  Format string as used in printf() */
  407. {
  408.     va_list     Args;
  409.     int         nLength;            /* Length of formatted string */
  410.     char        Buf[160];           /* Buffer to format string into */
  411.  
  412.     va_start(Args, szFormat);
  413.  
  414.     nLength = vsprintf( Buf, szFormat, Args );
  415.  
  416.     TextOut( hdcPaint, nPaintX, nPaintY+nExtLeading, Buf, nLength );
  417.     nPaintY += nCharSizeY;
  418.  
  419.     va_end(Args);
  420. }
  421.  
  422. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  423.  
  424. /*  Paints our window or any portion of it that needs painting.
  425.  *  The BeginPaint call sets up a structure that tells us what rectangle of the
  426.  *  window to paint, along with other information for the painting process.
  427.  *  First, erase the background area if necessary.
  428.  *  Then, calculate the index into the INFO array to start with, based on the
  429.  *  painting rectangle and the scroll bar position, and lock down the INFO.
  430.  *  Finally, loop through the INFO array, painting the text for each entry.
  431.  *  Quit when we run out of entries or hit the bottom of the paint rectangle.
  432.  */
  433.  
  434. void PASCAL PaintWindow( hWnd )
  435.     HWND        hWnd;               /* Window handle to paint */
  436. {
  437.     PAINTSTRUCT ps;                 /* Paint structure set up by BeginPaint */
  438.     DWORD       rgbOldTextColor;    /* Old text color (so we can restore it) */
  439.     DWORD       rgbOldBkColor;      /* Old background color */
  440.     int         nWin;               /* Index into INFO array */
  441.     int         X;                  /* X position for paint calculation */
  442.     int         Y;                  /* Y position for paint calculation */
  443.     PSTR        pTypeName;          /* Pointer to "Child", etc. string */
  444.  
  445.     /* Tell Windows we're painting, set up the paint structure. */
  446.     BeginPaint( hWnd, &ps );
  447.  
  448.     /* Store display context in global for Paint function */
  449.     hdcPaint = ps.hdc;
  450.  
  451.     /* Set up proper background and text colors and save old values */
  452.     rgbOldBkColor = SetBkColor( ps.hdc, GetSysColor( COLOR_WINDOW ) );
  453.     rgbOldTextColor = SetTextColor( ps.hdc, GetSysColor( COLOR_WINDOWTEXT ) );
  454.     /* Calculate horizontal paint position based on scroll bar position */
  455.     X = ( 1 - GetScrollPos( hWnd, SB_HORZ ) ) * nCharSizeX;
  456.  
  457.     /* Calculate index into INFO array and vertical paint position, based on
  458.      * scroll bar position and top of painting rectangle */
  459.     Y = GetScrollPos( hWnd, SB_VERT );
  460.     nWin = ( ps.rcPaint.top / nCharSizeY + Y ) / nLinesPerWindow;
  461.     nPaintY = ( nWin * nLinesPerWindow - Y ) * nCharSizeY;
  462.  
  463.     /* Lock down INFO array and set lpInfo pointing to first entry to paint */
  464.     lpInfo = (LPINFO)GlobalLock( hInfo );
  465.     lpInfo += nWin;
  466.  
  467.     /* Loop through INFO entries, painting each one until we run out of entries
  468.      * or until we are past the bottom of the paint rectangle.  We don't worry
  469.      * much about painting outside the rectangle - Windows will clip for us. */
  470.     while( nWin < nWindows  &&  nPaintY < ps.rcPaint.bottom )
  471.     {
  472.         /* Set X position and indent child windows, also set up pTypeName */
  473.         nPaintX = X;
  474.         if( lpInfo->winStyle & WS_CHILD ) {
  475.             nPaintX += nCharSizeX * ( bExpand ? 4 : 2 );
  476.             pTypeName = "Child";
  477.         } else if( lpInfo->winStyle & WS_ICONIC ) {
  478.             pTypeName = "Icon ";
  479.         } else if( lpInfo->winStyle & WS_POPUP ) {
  480.             pTypeName = "Popup";
  481.         } else {
  482.             pTypeName = "Top Level";
  483.         }
  484.  
  485.         if( ! bExpand ) {
  486.  
  487.             /* Paint the one-liner */
  488.             Paint(
  489.                 "%s window %04X {%Fs} (%d,%d;%d,%d) \"%Fs\"",
  490.                 pTypeName,
  491.                 lpInfo->winHWnd,
  492.                 lpInfo->winClass,
  493.                 lpInfo->winWindowRect.left,
  494.                 lpInfo->winWindowRect.top,
  495.                 lpInfo->winWindowRect.right,
  496.                 lpInfo->winWindowRect.bottom,
  497.                 lpInfo->winTitle
  498.             );
  499.  
  500.         } else {
  501.  
  502.             /* Paint the expanded form, first the window handle */
  503.             Paint(
  504.                 "%s window handle: %04X",
  505.                 pTypeName,
  506.                 lpInfo->winHWnd
  507.             );
  508.  
  509.             /* Paint the rest of the info, indented two spaces farther over */
  510.             nPaintX += nCharSizeX * 2;
  511.  
  512.             Paint( "Class name: %Fs", lpInfo->winClass );
  513.             Paint( "Window title: %Fs", lpInfo->winTitle );
  514.             Paint( "Parent window handle: %04X", lpInfo->winHWndParent );
  515.             Paint(
  516.                 "Class function, Window function: %p, %p",
  517.                 lpInfo->winClassProc,
  518.                 lpInfo->winWndProc
  519.             );
  520.             Paint(
  521.                 "Class module handle, Window instance handle: %04X, %04X",
  522.                 lpInfo->winClassModule,
  523.                 lpInfo->winInstance
  524.             );
  525.             Paint(
  526.                 "Class extra alloc, Window extra alloc: %d, %d",
  527.                 lpInfo->winClsExtra,
  528.                 lpInfo->winWndExtra
  529.             );
  530.             Paint(
  531.                 "Class style, Window style: %04X, %08lX",
  532.                 lpInfo->winClassStyle,
  533.                 lpInfo->winStyle
  534.             );
  535.             Paint(
  536.                 lpInfo->winStyle & WS_CHILD ?  "Control ID: %d" :
  537.                                                "Menu handle: %04X",
  538.                 lpInfo->winControlID
  539.             );
  540.             Paint(
  541.                 "Brush, Cursor, Icon handles: %04X, %04X, %04X",
  542.                 lpInfo->winBkgdBrush,
  543.                 lpInfo->winCursor,
  544.                 lpInfo->winIcon
  545.             );
  546.             Paint(
  547.                 "Window rectangle: Left=%4d, Top=%4d, Right=%4d, Bottom=%4d",
  548.                 lpInfo->winWindowRect.left,
  549.                 lpInfo->winWindowRect.top,
  550.                 lpInfo->winWindowRect.right,
  551.                 lpInfo->winWindowRect.bottom
  552.             );
  553.             Paint(
  554.                 "Client rectangle: Left=%4d, Top=%4d, Right=%4d, Bottom=%4d",
  555.                 lpInfo->winClientRect.left,
  556.                 lpInfo->winClientRect.top,
  557.                 lpInfo->winClientRect.right,
  558.                 lpInfo->winClientRect.bottom
  559.             );
  560.  
  561.             /* Make a blank line - it's already erased, so just increment Y */
  562.             nPaintY += nCharSizeY;
  563.         }
  564.  
  565.         /* Increment to next INFO entry */
  566.         ++nWin;
  567.         ++lpInfo;
  568.     }
  569.  
  570.     /* Unlock the INFO array */
  571.     GlobalUnlock( hInfo );
  572.  
  573.     /* Restore old colors */
  574.     SetBkColor( ps.hdc, rgbOldBkColor );
  575.     SetTextColor( ps.hdc, rgbOldTextColor );
  576.  
  577.     /* Tell Windows we're done painting */
  578.     EndPaint( hWnd, &ps );
  579. }
  580.  
  581. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  582.  
  583. /*  Set horizontal and vertical scroll bars, based on the window size and the
  584.  *  number of INFO entries.  The scroll bar ranges are set to give a total
  585.  *  width of WINDOW_WIDTH and a total height equal to the number of lines of
  586.  *  information available.  For example, if there are 130 lines of information
  587.  *  and the window height is 10 characters, the vertical scroll range is set
  588.  *  to 120 (130-10).  This lets you scroll through everything and still have
  589.  *  a full window of information at the bottom.  (Unlike, say, Windows Write,
  590.  *  where if you scroll to the bottom you have a blank screen.)
  591.  */
  592.  
  593. void PASCAL SetScrollBars( hWnd )
  594.     HWND        hWnd;               /* Window handle */
  595. {
  596.     RECT        rect;               /* The window's client rectangle */
  597.  
  598.     GetClientRect( hWnd, &rect );
  599.  
  600.     SetScrollBar1(
  601.         hWnd, SB_HORZ,
  602.         WINDOW_WIDTH - rect.right / nCharSizeX
  603.     );
  604.  
  605.     SetScrollBar1(
  606.         hWnd, SB_VERT,
  607.         nWindows * nLinesPerWindow - rect.bottom / nCharSizeY
  608.     );
  609. }
  610.  
  611. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  612.  
  613. /*  Set one scroll bar's maximum range.  We always set the minimum to zero,
  614.  *  although Windows allows other values.  There is one case we handle
  615.  *  specially.  If you set a scroll bar range to minimum==maximum (maximum =
  616.  *  zero for us), Windows does not actually set the range, but instead turns
  617.  *  off the scroll bar completely, changing the window style by turning off
  618.  *  the WS_HSCROLL or WS_VSCROLL bit.  For example, this is how the MS-DOS
  619.  *  Executive makes its scroll bars appear and disappear.  This behavior is
  620.  *  fine if you take it into account in your programming in two ways.  First,
  621.  *  whenever you do a GetScrollRange you must first check the window style to
  622.  *  see if that scroll bar still exists, because you will *not* get the correct
  623.  *  answer from GetScrollRange if it has been removed.  Second, you must be
  624.  *  prepared to get some extra WM_SIZE messages, because your client area
  625.  *  changes size when the scroll bars appear and disappear.  This can cause
  626.  *  some sloppy looking screen painting.  We take a different approach, always
  627.  *  keeping the scroll bars visible.  If the scroll bar range needs to be set
  628.  *  to zero, instead we set it to MAXINT so the bar remains visible.  Then, in
  629.  *  DoScrollMessage we check for this case and return without scrolling.
  630.  */
  631.  
  632. void PASCAL SetScrollBar1( hWnd, nBar, nMax )
  633.     HWND         hWnd;              /* Window handle */
  634.     int          nBar;              /* Which scroll bar: SB_HORZ or SB_VERT */
  635.     int          nMax;              /* Value to set maximum range to */
  636. {
  637.     int          nOldMin;           /* Previous minimum value (always 0) */
  638.     int          nOldMax;           /* Previous maximum value */
  639.  
  640.     /* Check for a negative or zero range and set our special case flag.
  641.      * Also, set the thumb position to zero in this case. */
  642.     if( nMax <= 0 ) {
  643.         nMax = MAXINT;
  644.         DoScrollMsg( hWnd, nBar, SB_THUMBPOSITION, 0 );
  645.     }
  646.  
  647.     /* Find out the previous range, and set it if it has changed */
  648.     GetScrollRange( hWnd, nBar, &nOldMin, &nOldMax );
  649.     if( nMax != nOldMax )
  650.         SetScrollRange( hWnd, nBar, 0, nMax, TRUE );
  651. }
  652.  
  653. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  654.  
  655. /*  Loop through all windows in the system and gather up information for the
  656.  *  INFO structure for each.  Use the EnumWindows and EnumChildWindows
  657.  *  functions to loop through them.  We actually loop through them twice:
  658.  *  first, to simply count them so we can allocate global memory for the
  659.  *  INFO structure, and again to actually fill in the structure.  After
  660.  *  gathering up the information, we invalidate our window, which will cause
  661.  *  a WM_PAINT message to be posted, so it will get repainted.
  662.  */
  663.  
  664. BOOL PASCAL SpyOnAllWindows( hWnd )
  665.     HWND        hWnd;               /* Window handle */
  666. {
  667.     /* Calculate the number of windows and amount of memory needed */
  668.     nWindows = 0;
  669.     dwInfoSize = 0;
  670.     EnumWindows( lpprocCountWindow, 1L );
  671.  
  672.     /* Allocate the memory, complain if we couldn't get it */
  673.     hInfo = GlobalReAlloc( hInfo, dwInfoSize, GMEM_MOVEABLE );
  674.     if( ! hInfo ) {
  675.         nWindows = 0;
  676.         dwInfoSize = 0;
  677.         GlobalDiscard( hInfo );
  678.         MessageBox(
  679.             GetActiveWindow(),
  680.             "Insufficient memory!!",
  681.             NULL,
  682.             MB_OK | MB_ICONHAND
  683.         );
  684.         PostQuitMessage( 0 );
  685.         return FALSE;
  686.     }
  687.  
  688.     /* Lock down the memory and fill in the information, then unlock it */
  689.     lpInfo = (LPINFO)GlobalLock( hInfo );
  690.     EnumWindows( lpprocSpyOnWindow, 1L );
  691.     GlobalUnlock( hInfo );
  692.  
  693.     /* Set the scroll bars based on new window count, repaint our window */
  694.     SetScrollBars( hWnd );
  695.     HomeScrollBars( hWnd, TRUE );
  696.     InvalidateRect( hWnd, NULL, TRUE );
  697.  
  698.     return TRUE;
  699. }
  700.  
  701. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  702.  
  703. /*  Enumeration function to gather up the information for a single window and
  704.  *  store it in the INFO array entry pointed to by lpInfo.  Increment lpInfo
  705.  *  to the next entry afterward.  Called once for each window, via EnumWindows
  706.  *  for each top level and popup window, and recursively via EnumChildWindows
  707.  *  for child windows.  The lTopLevel parameter tells which kind of call it is.
  708.  */
  709.  
  710. BOOL FAR PASCAL SpyOnWindow( hWnd, lTopLevel )
  711.     HWND        hWnd;               /* Window handle */
  712.     long        lTopLevel;          /* 1=top level window, 0=child window */
  713. {
  714.     /* Gather up this window's information */
  715.     lpInfo->winHWnd = hWnd;
  716.     GetClassName( hWnd, lpInfo->winClass, CLASSMAX );
  717.     lpInfo->winClass[ CLASSMAX - 1 ] = 0;
  718.     lpInfo->winInstance = GetWindowWord( hWnd, GWW_HINSTANCE );
  719.     lpInfo->winHWndParent = GetParent( hWnd );
  720.     GetWindowText( hWnd, lpInfo->winTitle, TITLEMAX );
  721.     lpInfo->winTitle[ TITLEMAX - 1 ] = 0;
  722.     lpInfo->winControlID = GetWindowWord( hWnd, GWW_ID );
  723.     lpInfo->winWndProc = (FARPROC)GetWindowLong( hWnd, GWL_WNDPROC );
  724.     lpInfo->winStyle = GetWindowLong( hWnd, GWL_STYLE );
  725.     GetClientRect( hWnd, &lpInfo->winClientRect );
  726.     GetWindowRect( hWnd, &lpInfo->winWindowRect );
  727.  
  728.     /* Gather up class information */
  729.     lpInfo->winBkgdBrush = GetClassWord( hWnd, GCW_HBRBACKGROUND );
  730.     lpInfo->winCursor = GetClassWord( hWnd, GCW_HCURSOR );
  731.     lpInfo->winIcon = GetClassWord( hWnd, GCW_HICON );
  732.     lpInfo->winClassModule = GetClassWord( hWnd, GCW_HMODULE );
  733.     lpInfo->winWndExtra = GetClassWord( hWnd, GCW_CBWNDEXTRA );
  734.     lpInfo->winClsExtra = GetClassWord( hWnd, GCW_CBCLSEXTRA );
  735.     lpInfo->winClassStyle = GetClassWord( hWnd, GCW_STYLE );
  736.     lpInfo->winClassProc = (FARPROC)GetClassLong( hWnd, GCL_WNDPROC );
  737.  
  738.     /* Move on to next entry in table */
  739.     ++lpInfo;
  740.  
  741.     /* If it's a top level window, get its children too */
  742.     if( lTopLevel )
  743.         EnumChildWindows( hWnd, lpprocSpyOnWindow, 0L );
  744.  
  745.     return TRUE;  /* TRUE to continue enumeration */
  746. }
  747.  
  748. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  749.  
  750. /*  Window function for our main window.  All messages for our window are sent
  751.  *  to this function.  For messages that we do not handle here, we call
  752.  *  DefWindowProc, which performs Windows' default processing for a message.
  753.  */
  754.  
  755. long FAR PASCAL SpyWndProc( hWnd, wMsg, wParam, lParam )
  756.     HWND        hWnd;               /* Window handle */
  757.     unsigned    wMsg;               /* Message number */
  758.     WORD        wParam;             /* Word parameter for the message */
  759.     LONG        lParam;             /* Long parameter for the message */
  760. {
  761.     RECT        rect;               /* A rectangle */
  762.  
  763.     switch( wMsg ) {
  764.  
  765.         /* Menu command message - process the command */
  766.         case WM_COMMAND:
  767.             if( LOWORD(lParam) )
  768.                 break;  /* not a command */
  769.             switch( wParam ) {
  770.                 case CMD_EXPAND:
  771.                     bExpand = ! bExpand;
  772.                     nLinesPerWindow = ( bExpand ? LINES_PER_WINDOW : 1 );
  773.                     CheckMenuItem(
  774.                         GetMenu( hWnd ),
  775.                         CMD_EXPAND,
  776.                         bExpand ? MF_CHECKED : MF_UNCHECKED
  777.                     );
  778.                     InvalidateRect( hWnd, NULL, TRUE );
  779.                     HomeScrollBars( hWnd, FALSE );
  780.                     SetScrollBars( hWnd );
  781.                     return 0L;
  782.                 case CMD_SPY:
  783.                     SpyOnAllWindows( hWnd );
  784.                     return 0L;
  785.                 default:
  786.                     break;
  787.             }
  788.             break;
  789.  
  790.         /* Destroy-window message - time to quit the application */
  791.         case WM_DESTROY:
  792.             PostQuitMessage( 0 );
  793.             return 0L;
  794.  
  795.         /* Horizontal scroll message - scroll the window */
  796.         case WM_HSCROLL:
  797.             DoScrollMsg( hWnd, SB_HORZ, wParam, (int)lParam );
  798.             return 0L;
  799.  
  800.         /* Key-down message - handle cursor keys, ignore other keys */
  801.         case WM_KEYDOWN:
  802.             if( wParam >= VK_MIN_CURSOR  &&  wParam <= VK_MAX_CURSOR ) {
  803.                 DoScrollMsg(
  804.                     hWnd,
  805.                     CsrScroll[ wParam - VK_MIN_CURSOR ].csBar,
  806.                     CsrScroll[ wParam - VK_MIN_CURSOR ].csMsg,
  807.                     0
  808.                 );
  809.             }
  810.             return 0L;
  811.  
  812.         /* Paint message - repaint all or part of our window */
  813.         case WM_PAINT:
  814.             PaintWindow( hWnd );
  815.             return 0L;
  816.  
  817.         /* Size message - recalculate our scroll bars to take the new size
  818.          * into account, but only if initialization has been completed.  There
  819.          * are several superfluous WM_SIZE messages sent during initialization,
  820.          * and it looks ugly if we repaint the scroll bars for all these. */
  821.         case WM_SIZE:
  822.             if( bInitted )
  823.                 SetScrollBars( hWnd );
  824.             return 0L;
  825.  
  826.         /* Vertical scroll message - scroll the window */
  827.         case WM_VSCROLL:
  828.             DoScrollMsg( hWnd, SB_VERT, wParam, (int)lParam );
  829.             return 0L;
  830.  
  831.         /* For all other messages, we pass them on to DefWindowProc */
  832.         default:
  833.             break;
  834.     }
  835.     return DefWindowProc( hWnd, wMsg, wParam, lParam );
  836. }
  837.  
  838. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  839.  
  840. /*  Application main program.  Not much is done here - we just initialize
  841.  *  the application, putting up our window, and then we go into the typical
  842.  *  message dispatching loop that every Windows application has.
  843.  */
  844.  
  845. int PASCAL WinMain( hInst, hPrevInst, lpszCmdLine, nCmdShow )
  846.     HANDLE      hInst;              /* Our instance handle */
  847.     HANDLE      hPrevInst;          /* Previous instance of this application */
  848.     LPSTR       lpszCmdLine;        /* Pointer to any command line params */
  849.     int         nCmdShow;           /* Parameter to use for first ShowWindow */
  850. {
  851.     MSG         msg;                /* Message structure */
  852.  
  853.     /* Save our instance handle in static variable */
  854.     hInstance = hInst;
  855.  
  856.     /* Initialize application, quit if any errors */
  857.     if( ! Initialize( hPrevInst, nCmdShow ) )
  858.         return 1;
  859.  
  860.     /* Main message processing loop.  Get each message, then translate keyboard
  861.      * messages, and finally dispatch each message to its window function. */
  862.     while( GetMessage( &msg, NULL, 0, 0 ) ) {
  863.         TranslateMessage( &msg );
  864.         DispatchMessage( &msg );
  865.     }
  866.  
  867.     return msg.wParam;
  868. }
  869.  
  870. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  871.