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