home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 10 / 10.iso / l / l430 / 1.ddi / SOURCE.ZIP / SNOOP.C < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-10  |  36.9 KB  |  1,240 lines

  1. /*
  2.     SNOOP.C -- Windows Message Interceptor and Trace Utility
  3.  
  4.     Copyright (c) Dave Maxey, 1992 
  5.     
  6.     From Chapter 4 of "Undocumented Windows" (Addison-Wesley 1992)
  7.     by Andrew Schulman, Dave Maxey and Matt Pietrek
  8.  
  9.     Build using: WINIOBC SNOOP I3HANDLR (for Borland C++ v3.00)
  10.                  WINIOMS SNOOP I3HANDLR (for Microsoft C/SDK)
  11.     Note: Ensure that SNOOP.DEF is present
  12. */ 
  13.  
  14. #include <windows.h> 
  15. #include <wmhandlr.h> 
  16. #include <winio.h> 
  17. #include <io.h> 
  18. #include <stdlib.h> 
  19. #include <string.h> 
  20. #include <malloc.h> 
  21. #include <dos.h> 
  22. #include "..\wnd.h" 
  23.  
  24. #ifndef __BORLANDC__ 
  25. #define MK_FP(a,b)  ((void far *)(((unsigned long)(a) << 16) | (b))) 
  26. #else 
  27. #define asm _asm 
  28. #endif 
  29.  
  30. #define     ID_BUILDLIST    1 
  31. #define     ID_HELP     2 
  32. #define     ID_CLEAR        3 
  33. #define     ID_FILTER       4 
  34. #define     ID_TRACE        5 
  35. #define     MAX_UNDOCWMS    256 
  36. #define     MAX_DOCWMS      256 
  37. #define     MAX_WNDS        256 
  38. #define     MAX_QUEUE       100 
  39. #define     MAX_STACK       32 
  40. #define     SNOOPBUF        8192 
  41. #define     INT_3           0xCC 
  42. #define     MENUCLASS       0x8000
  43. #define     DESKTOPCLASS    0x8001
  44. #define     DIALOGCLASS     0x8002
  45. #define     SWITCHWNDCLASS  0x8003
  46. #define     ICONTITLECLASS  0x8004
  47.  
  48.  
  49. extern WORD FAR PASCAL SysErrorBox(LPSTR, LPSTR, WORD, WORD, WORD); 
  50.  
  51. BOOL b3_1 = TRUE;   // needed for MACROs below
  52.  
  53. /* MACROS to navigate the SendMessage history - See Chapter 5 */
  54.  
  55. // Get the task queue handle of the task that sent 'h' its
  56. // current message. This info is held at offset 0x3A in the
  57. // TASKQUEUE structure. If not in a message, the field will
  58. // contain NULL.
  59. #ifdef HMMMM
  60. #define SENDER_TO(h) \
  61.     (* (WORD FAR *) MK_FP((WORD) h, b3_1 ? 0x3A : 0x38))
  62. #else
  63. #define SENDER_TO(h) \
  64.     (* (WORD FAR *) MK_FP((WORD) (h & 0xfffc) | 1, b3_1 ? 0x3A : 0x38))
  65. #endif
  66.  
  67. // Get the hInstance of the task whose task
  68. // queue handle is 'h' by lifting the task database handle from
  69. // offset 2 of the task queue sructure, and then passing offset
  70. // 0x1C of the task database structure.
  71. #ifdef HMMMM
  72. #define HINST_TASKQ(h) \
  73.     (* (HANDLE FAR *) MK_FP((* (HANDLE FAR *) MK_FP(h, 2)), 0x1C))
  74. #else
  75. #define HINST_TASKQ(h) \
  76.     (* (HANDLE FAR *) MK_FP(((* (HANDLE FAR *) MK_FP((h & 0xfffc) | 1, 2)) & 0xfffc) | 1, 0x1C))
  77. #endif
  78.  
  79.  
  80. extern void FAR Int3Handler(void); 
  81. extern void FAR WndProcExit(void); 
  82. extern WORD FAR PASCAL AllocCStoDSAlias(WORD wSel); 
  83. extern WORD FAR PASCAL AllocDStoCSAlias(WORD wSel); 
  84. extern HANDLE FAR PASCAL GetTaskQueue(HANDLE hTask); 
  85. extern HANDLE FAR PASCAL GetCurrentTask(void); 
  86.  
  87. typedef struct tagProcHook { 
  88.     LPSTR lpszProc; 
  89.     LPSTR lpszClass; 
  90.     FARPROC lpfnProc;
  91.     BYTE by1stCode; 
  92.     } PROCINFO; 
  93.  
  94. typedef struct tagUndocWM { 
  95.     WORD wWMFrom; 
  96.     WORD wWMTo; 
  97.     LPSTR szWM; 
  98.     } UNDOCWM; 
  99.  
  100. typedef struct tagDocWM { 
  101.     WORD wWM; 
  102.     LPSTR szWM; 
  103.     } DOCWM; 
  104.  
  105. typedef struct tagSnoopInfo { 
  106.     HWND hwndTarget; 
  107.     HWND hwndSnoop; 
  108.     FARPROC oldWndProc; 
  109.     WORD wNum; 
  110.     HMENU hMenuOpts; 
  111.     BOOL bFilter; 
  112.     BOOL bTrace; 
  113.     } SNOOPINFO; 
  114.  
  115. typedef struct tagStackEntry { 
  116.     FARPROC fpCSIP; 
  117.     LPSTR lpszProc; 
  118.     HWND hwnd; 
  119.     LPSTR lpszMsg; 
  120.     DWORD lpStack;
  121.     } STACKENTRY; 
  122.      
  123. typedef char q_entry[80]; 
  124.  
  125. char szAppTitle[] = "Windows Message Snoop"; 
  126. char szWM_SNOOPING[] = "WM_SNOOPING"; 
  127. WORD wWM_SNOOPING; 
  128. int nUNDOCWMs = 0; 
  129. int nDOCWMs = 0; 
  130. int nHWNDs;
  131.  
  132. HWND hwndList[MAX_WNDS]; 
  133.  
  134. SNOOPINFO snoopinfo[MAX_WNDS]; 
  135.  
  136. UNDOCWM undocwm[MAX_UNDOCWMS]; 
  137.  
  138. DOCWM docwm[MAX_DOCWMS]; 
  139.  
  140. PROCINFO procinfo[] = 
  141. /*      WINDOW PROCEDURE        CLASS           ENTRY  1stBYTE */ 
  142.     {   "DefWindowProc",        NULL,           NULL,   0,  
  143.         "DefDlgProc",           NULL,           NULL,   0,  
  144.         "DefMDIChildProc",      NULL,           NULL,   0,  
  145.         "DefFrameProc",         NULL,           NULL,   0,  
  146.         "EditWndProc",          "EDIT",         NULL,   0,  
  147.         "ButtonWndProc",        "BUTTON",       NULL,   0,  
  148.         "ComboBoxCtlWndProc",   "COMBOBOX",     NULL,   0,  
  149.         "LBoxCtlWndProc",       "LISTBOX",      NULL,   0,  
  150.         "MDIClientWndProc",     "MDICLIENT",    NULL,   0,  
  151.         "SBWndProc",            "SCROLLBAR",    NULL,   0,  
  152.         "StaticWndProc",        "STATIC",       NULL,   0,
  153.         "DesktopWndProc",(LPSTR) DESKTOPCLASS,  NULL,   0,
  154.         "MenuWndProc",  (LPSTR) MENUCLASS,      NULL,   0,
  155.         }; 
  156. #define     MAX_PROCS   (sizeof(procinfo) / sizeof(PROCINFO))
  157.  
  158. STACKENTRY stack[MAX_STACK] = {0}; 
  159. // int nSP = 0;
  160. //WORD wCounter = 1;
  161. int nIndent = 0;
  162. WORD wSelUserDS; 
  163. FARPROC lpfnSnoopFunc; 
  164. FARPROC lpfnInt3Handler; 
  165. FARPROC lpfnWndProcExit; 
  166. HWND qWin[MAX_QUEUE]; 
  167. q_entry szQueue[MAX_QUEUE]; 
  168. int q_head = 0, q_tail = 0; 
  169. FARPROC ifOld = NULL; 
  170. DWORD cMissed = 0L; 
  171. HWND hwndCurr; 
  172. char strIndent[MAX_STACK] = {0}; 
  173.  
  174.  
  175. // Display an error message box 
  176. BOOL error(char *sz) 
  177.     { 
  178.     winio_warn(FALSE, szAppTitle, sz); 
  179.     return FALSE; 
  180.     } 
  181.  
  182.  
  183. void GetExeName(HANDLE hInst, LPSTR strName)
  184.     {
  185.     static char str[128];
  186.     int i;
  187.     
  188.     GetModuleFileName(hInst, str, 128);
  189.     i = strlen(str) - 1;
  190.     
  191.     for ( ; i && (str[i] != '\\'); i--)
  192.         if (str[i] == '.') str[i] = 0;
  193.     
  194.     if (str[i] == '\\') i++;
  195.  
  196.     // We cannot use strcpy in SMALL model - Assumes SS==DS
  197.     lstrcpy(strName, (LPSTR) &str[i]);
  198.     }
  199.  
  200.  
  201. void GetAnyAtomName(WORD wSeg, ATOM nAtom, LPSTR lpBuffer, int nSize)
  202.     {
  203.     if (! nAtom)
  204.         {
  205.         *lpBuffer = 0;
  206.         return;
  207.         }
  208.     _asm push ds;
  209.     _asm mov ds, word ptr wSeg;
  210.     GetAtomName(nAtom, lpBuffer, nSize);
  211.     _asm pop ds;
  212.     }
  213.  
  214.  
  215. // Get a display queue entry to be fed to monitor window hwnd 
  216. // or return NULL if the queue is full. 
  217. LPSTR get_q_head(HWND hwnd) 
  218.     { 
  219.     int q_tmp = q_head; 
  220.      
  221.     if (++q_head == MAX_QUEUE) q_head = 0; 
  222.     if (q_head == q_tail) 
  223.         { 
  224.         cMissed++; 
  225.         return NULL;    // queue is full ! 
  226.         } 
  227.     else 
  228.         { 
  229.         qWin[q_tmp] = hwnd; 
  230.         return (LPSTR) &szQueue[q_tmp]; 
  231.         } 
  232.     } 
  233.  
  234.  
  235. // Get an entry from the display queue, and the hwnd that 
  236. // it is to be displayed upon. 
  237. char *get_q_tail(HWND *phwnd) 
  238.     { 
  239.     int q_tmp = q_tail; 
  240.      
  241.     if (q_tail == q_head) return NULL; 
  242.     if (++q_tail == MAX_QUEUE) 
  243.         q_tail = 0; 
  244.     *phwnd = qWin[q_tmp]; 
  245.     return szQueue[q_tmp];  
  246.     } 
  247.  
  248.  
  249.  
  250. // Call DPMI to tell us where protected mode interrupt intno 
  251. // currently points. 
  252. FARPROC _dpmi_getvect(unsigned intno) 
  253.     { 
  254.     _asm { 
  255.         mov ax, 0204h 
  256.         mov bl, byte ptr intno 
  257.         int 31h 
  258.         /* return CX:DX in DX:AX */ 
  259.         mov ax, dx 
  260.         mov dx, cx 
  261.         } 
  262.     } 
  263.  
  264.  
  265. // Call DPMI to install our handler for protected mode interrupt 
  266. // intno. Use the above function to check that it worked. 
  267. BOOL _dpmi_setvect(unsigned intno, FARPROC handler) 
  268.     { 
  269.     _asm { 
  270.         mov ax, 0205h 
  271.         mov bl, byte ptr intno 
  272.         mov cx, word ptr handler+2 
  273.         mov dx, word ptr handler 
  274.         int 31h 
  275.         } 
  276.     return (_dpmi_getvect(intno) == handler); 
  277.     } 
  278.  
  279.  
  280.  
  281. WORD verr(WORD sel)
  282.     {
  283.     _asm mov ax, 1
  284.     _asm verr word ptr sel
  285.     _asm je short ok
  286.     _asm dec ax
  287.     ok:;
  288.     }
  289.  
  290.  
  291.  
  292. // This is a special version of MakeProcInstance. We use it to 
  293. // create the procedure instance, or 'thunk', for the function 
  294. // that will be called when any of the hooked default window 
  295. // procedure functions return. The regular thunk sets the 
  296. // instance's DGROUP selector value into AX ready for the 
  297. // function prolog to MOV DS, AX. This would trash whatever 
  298. // return code is being passed back from the hooked function. 
  299. // Since our handler function had to be written in Assembler anyway, 
  300. // we can use our own prolog code to take the DGROUP value from BX, 
  301. // where our 'thunk' puts it. 
  302. FARPROC MakeProcInstanceBX(FARPROC fp, HANDLE h) 
  303.     { 
  304.  
  305. #pragma pack(1) 
  306.     struct { BYTE mov_bx; WORD h; BYTE jmp_far; FARPROC fp; } *pthunk; 
  307. #pragma pack() 
  308.  
  309.     WORD hCS; 
  310.      
  311.     if ((! (pthunk = malloc(sizeof(*pthunk)))) || 
  312.         (! (hCS = AllocDStoCSAlias(h)))) 
  313.         return NULL; 
  314.      
  315.     pthunk->mov_bx = 0xBB; 
  316.     pthunk->h = h; 
  317.     pthunk->h &= 0xfffc; 
  318.     pthunk->h |= 1; 
  319.     pthunk->jmp_far = 0xEA; 
  320.     pthunk->fp = fp; 
  321.     return (FARPROC) MK_FP(hCS, (WORD) pthunk); 
  322.     } 
  323.  
  324.  
  325. LPSTR MsgName(WORD wMsg) 
  326.     { 
  327.     int i; 
  328.  
  329.     // If this is WM_USER + ??? return generic message 
  330.     if (wMsg >= WM_USER) return (LPSTR) "WM_USER+"; 
  331.      
  332.     // Look for message in the documented list first 
  333.     for (i = 0; i < nDOCWMs; i++) 
  334.         if (wMsg == docwm[i].wWM) 
  335.             return docwm[i].szWM; 
  336.  
  337.     // Look for message in the undocumented list 
  338.     for (i = 0; i < nUNDOCWMs; i++) 
  339.         if ((wMsg >= undocwm[i].wWMFrom) && 
  340.             (wMsg <= undocwm[i].wWMTo)) 
  341.             return undocwm[i].szWM; 
  342.  
  343.     return (LPSTR) "*WM_?*"; 
  344.     } 
  345.  
  346.  
  347.  
  348. BYTE SmackCode(FARPROC lpfn, BYTE byInst)
  349.     {
  350.     BYTE byPrev;
  351.     HANDLE hData, hDataRaw; 
  352.  
  353.     // Ensure that the code hangs around for a second
  354.     GetCodeHandle(lpfn);
  355.  
  356.     // And lock everything down
  357.     GlobalPageLock(FP_SEG(lpfn));
  358.     
  359.     // Get a usable selector for code read/write 
  360.     hDataRaw = AllocCStoDSAlias(FP_SEG(lpfn)); 
  361.          
  362.     // Adjust the selector privilege level for 3.0 
  363.     hData = (hDataRaw & 0xfffc) | 1; 
  364.  
  365.     // Trade in the new instruction
  366.     byPrev = *((LPBYTE) MK_FP(hData, FP_OFF(lpfn))); 
  367.     *((LPBYTE) MK_FP(hData, FP_OFF(lpfn))) = byInst;
  368.     
  369.     // Unlock the code
  370.     GlobalUnlock(FP_SEG(lpfn));
  371.     
  372.     // Free the alias selector
  373.     FreeSelector(hDataRaw);
  374.  
  375.     return byPrev;
  376.     }
  377.  
  378.  
  379. // The following procedure must be cdecl. On the stack is the return 
  380. // code from the DefXXXXXProc that has returned to us. We must return 
  381. // the return address that was there before we hooked ourselves in... 
  382. FARPROC WndProcExitProc( DWORD lpStack, WORD hCallerDS, DWORD lRet) 
  383.     { 
  384.     int j, i; 
  385.     LPSTR lpszTmp;
  386.     
  387.     for (i = 0; i < MAX_STACK; i++)
  388.         if ((lpStack == stack[i].lpStack) ||
  389.             ((lpStack + 2) == stack[i].lpStack)) break;
  390.     
  391.     if (i == MAX_STACK)
  392.         SysErrorBox("Unmatched return - System will now crash!",
  393.             szAppTitle, 0, 0x8001, 0);
  394.  
  395. //  if (! nSP--)
  396. //      SysErrorBox("Stack underflow - System will now crash!",
  397. //          szAppTitle, 0, 0x8001, 0);
  398. //  if (lpStack != stack[i].lpStack)
  399. //      SysErrorBox("Stack mismatch - System will now crash!",
  400. //          szAppTitle, 0, 0x8001, 0);
  401.  
  402. //  SysErrorBox("Return", stack[nSP].lpszProc, 0, 1, 0);
  403.     
  404.     // Try and reinstate Int 3 for all hooks 
  405.     for (j = 0; j < MAX_PROCS; j++) 
  406.         if (! procinfo[j].by1stCode)
  407.             procinfo[j].by1stCode = SmackCode(procinfo[j].lpfnProc, INT_3);
  408.      
  409.     // Locate info about this target window 
  410.     for (j = 0; j < MAX_WNDS; j++) 
  411.         if (snoopinfo[j].hwndTarget == hwndCurr) 
  412.             break; 
  413.      
  414.     // Record the return in the display queue 
  415.     strIndent[--nIndent] = 0; 
  416.     if ((j != MAX_WNDS) && 
  417.         (lpszTmp = get_q_head(snoopinfo[j].hwndSnoop))) 
  418.         wsprintf(lpszTmp, "    %s<-%s ret %08lX (h=%04X m=%s)", 
  419.             (LPSTR) strIndent, stack[i].lpszProc, lRet, 
  420.             stack[i].hwnd, stack[i].lpszMsg); 
  421.  
  422.     stack[i].lpStack = NULL;
  423.     return stack[i].fpCSIP; 
  424.     } 
  425.  
  426.  
  427.  
  428. // The following procedure must be cdecl.  
  429. void Int3EntryProc(DWORD lpStack, WORD wCallerDS, FARPROC fpWndProc,   
  430.                 WORD wFlags, FARPROC fpCallerCSIP,  
  431.                 DWORD lParam, WORD wParam, WORD wMsg, HWND hwnd)  
  432.                      
  433.     { 
  434.     LPSTR lpszTmp, lpszMsg; 
  435.     int i, j, k; 
  436.  
  437.     for (i = 0; i < MAX_PROCS; i++) 
  438.         if (fpWndProc == procinfo[i].lpfnProc) 
  439.             break; 
  440.  
  441.     if (i == MAX_PROCS)
  442.         SysErrorBox("Unrecognized INT 3 - Crash coming!",
  443.             szAppTitle, 0, 0x8001, 0);
  444.     
  445. //  SysErrorBox("Entry", procinfo[i].lpszProc, 0, 1, 0);
  446.  
  447.     for (k = 0; k < MAX_STACK; k++)
  448.         if (stack[k].lpStack == NULL) break;
  449.     
  450.     // Save away on our save 'stack' return address for exit proc 
  451.     // and smack exit proc onto stack in its place 
  452.     if (k == MAX_STACK) 
  453.         SysErrorBox("Stack overrun - The system will now crash!",
  454.             szAppTitle, 0, 0x8001, 0); 
  455.      
  456.     // Reinstate instruction in place of int 3 for current hook, 
  457.     // and ensure int 3s in place for all other hooks 
  458.     for (j = 0; j < MAX_PROCS; j++) 
  459.         { 
  460.         if (j == i)
  461.             {
  462.             if (procinfo[j].by1stCode)
  463.                 {
  464.                 SmackCode(procinfo[j].lpfnProc, procinfo[j].by1stCode);
  465.                 procinfo[j].by1stCode = 0;
  466.                 }
  467.             }
  468.         else
  469.         if (! procinfo[j].by1stCode)
  470.             procinfo[j].by1stCode = SmackCode(procinfo[j].lpfnProc, INT_3);
  471.         } 
  472.      
  473.     // Locate info about this target window 
  474.     for (j = 0; j < MAX_WNDS; j++) 
  475.         if (snoopinfo[j].hwndTarget == hwndCurr) 
  476.             break; 
  477.  
  478.     // Get message WM_ name 
  479.     lpszMsg = MsgName(wMsg); 
  480.      
  481.     // Record entry in the display queue 
  482.     if ((j != MAX_WNDS) && 
  483.         (lpszTmp = get_q_head(snoopinfo[j].hwndSnoop))) 
  484.         wsprintf(lpszTmp, "    %s->%s h=%04X m=%s(%04X) w=%04X l=%08lX", 
  485.             (LPSTR) strIndent, procinfo[i].lpszProc, 
  486.             hwnd, lpszMsg, wMsg, wParam, lParam); 
  487.      
  488.     // Store information we will need at hooked function return time 
  489.     strIndent[nIndent++] = ' '; 
  490.     stack[k].lpszProc = procinfo[i].lpszProc; 
  491.     stack[k].fpCSIP = fpCallerCSIP; 
  492.     stack[k].lpszMsg = lpszMsg; 
  493.     stack[k].hwnd = hwnd; 
  494.     stack[k].lpStack = lpStack;
  495.  
  496.     // Hook our function in as return address for hooked function 
  497.     fpCallerCSIP = lpfnWndProcExit; 
  498.     } 
  499.  
  500.  
  501.  
  502. // Int 3 is how tracing is accomplished if the 'trace' option is 
  503. // selected. First job when a listed message is intercepted in trace 
  504. // mode is to establish our Int 3 handler, and set int 3s into all 
  505. // default window function entrypoints. Note that it would be a problem 
  506. // if we installed an int 3 handler for a second or subsequent 
  507. // time, as we would overwrite the saved vector with our own handler 
  508. // address. 
  509. void SetupInt3(void) 
  510.     { 
  511.     int i; 
  512.  
  513.     if (! ifOld)
  514.         {
  515.         ifOld = _dpmi_getvect(3); 
  516.         if (! _dpmi_setvect(3, lpfnInt3Handler)) 
  517.             { 
  518.             error("Could not hook Int 3!"); 
  519.             return; 
  520.             }
  521.         }
  522.      
  523.     for (i = 0; i < MAX_PROCS; i++) 
  524.         if (! procinfo[i].by1stCode)
  525.             procinfo[i].by1stCode = SmackCode(procinfo[i].lpfnProc, INT_3);
  526.     } 
  527.  
  528.  
  529. // Unhook all int 3s, reinstating original instructions. Then 
  530. // restore previous int 3 handler. 
  531. void RestoreInt3(void) 
  532.     { 
  533.     int i; 
  534.  
  535.      
  536.     for (i = 0; i < MAX_PROCS; i++) 
  537.         if (procinfo[i].by1stCode)
  538.             {
  539.             SmackCode(procinfo[i].lpfnProc, procinfo[i].by1stCode);
  540.             procinfo[i].by1stCode = 0;
  541.             }
  542.      
  543.     if (ifOld)
  544.         {
  545.         if (! _dpmi_setvect(3, ifOld)) 
  546.             error("Could not restore Int 3!"); 
  547.         ifOld = NULL;
  548.         }
  549.     } 
  550.  
  551.  
  552. // Display a window hierarchy on the main window. Calls itself 
  553. // recursively for child windows. 
  554. void printtree(WORD wndofs, int *pi, char *strIndent) 
  555.     {
  556.     WND_3_0 FAR *hwnd30; 
  557.     WND_3_1 FAR *hwnd31; 
  558.     CLASS FAR *pcls;
  559.     WORD clsofs;
  560.     char strText[128];
  561.     char strClass[30];
  562.     int i; 
  563.  
  564.     i = *pi; 
  565.      
  566.     for (;;) 
  567.         { 
  568.         // add to the global list of HWNDs 
  569.         hwndList[i++] = wndofs; 
  570.         (void far *) hwnd31 = (void far *) hwnd30 = MK_FP(wSelUserDS, wndofs);
  571.         clsofs = b3_1 ? (WORD) (hwnd31->hClass) : (WORD) (hwnd30->hClass);
  572.         (void far *) pcls = MK_FP(wSelUserDS, clsofs);
  573.         if (pcls->wSig != 0x4b4e)
  574.             winio_warn(FALSE, __szModule, "Bad class pointer");
  575.         GetWindowText((HWND) wndofs,
  576.             strText, sizeof(strText));
  577.         GetAnyAtomName(wSelUserDS, pcls->atomCls,
  578.             (LPSTR) &strClass, sizeof(strClass));
  579.         if (strClass[0] == '#')
  580.             GlobalGetAtomName(pcls->atomCls /* & 0x7fff */,
  581.                 (LPSTR) &strClass, sizeof(strClass));
  582.         printf("%s%04X %s (%s)\n", strIndent, wndofs, strText, strClass); 
  583.         if (hwnd30->hwndChild != NULL) 
  584.             { 
  585.             strIndent -= 4; 
  586.             printtree(hwnd30->hwndChild, &i, strIndent); 
  587.             strIndent += 4; 
  588.             } 
  589.  
  590.         if ((wndofs = hwnd30->hwndNext) == NULL) 
  591.             break; 
  592.         } 
  593.     *pi = i; 
  594.     } 
  595.  
  596.  
  597.  
  598. // Reset a monitoring window's history 
  599. void doclear(HWND hwnd, int nID) 
  600.     { 
  601.     int i; 
  602.  
  603.     // Locate info about window 
  604.     for (i = 0; snoopinfo[i].hwndSnoop != hwnd; i++) ; 
  605.  
  606.     snoopinfo[i].wNum = 0; 
  607.     winio_clear(hwnd); 
  608.     } 
  609.  
  610.  
  611.  
  612. // Toggle the 'filter' menu option. 
  613. void dofilter(HWND hwnd, int nID) 
  614.     { 
  615.     int i; 
  616.  
  617.     // Locate info about window 
  618.     for (i = 0; snoopinfo[i].hwndSnoop != hwnd; i++) ; 
  619.  
  620.     CheckMenuItem(snoopinfo[i].hMenuOpts, ID_FILTER, 
  621.         snoopinfo[i].bFilter ^= -1 ? MF_CHECKED : MF_UNCHECKED); 
  622.     } 
  623.  
  624.  
  625.      
  626. // Toggle the 'trace' menu option. 
  627. void dotrace(HWND hwnd, int nID) 
  628.     { 
  629.     int i; 
  630.  
  631.     // Locate info about window 
  632.     for (i = 0; snoopinfo[i].hwndSnoop != hwnd; i++) ; 
  633.  
  634.     CheckMenuItem(snoopinfo[i].hMenuOpts, ID_TRACE, 
  635.         snoopinfo[i].bTrace ^= -1 ? MF_CHECKED : MF_UNCHECKED); 
  636.     } 
  637.  
  638.  
  639.  
  640. // Respond to a request to build the window handle tree 
  641. void buildlist(HWND hwnd, int nID) 
  642.     { 
  643.     HWND hwndSave = winio_setcurrent(__hMainWnd); 
  644.     char *strIndent = "                                "; 
  645.  
  646.     winio_clear(__hMainWnd); 
  647.     winio_setpaint(__hMainWnd, FALSE); 
  648.      
  649.     
  650.  
  651.     // Initialize the global llist of HWNDs 
  652.     memset(hwndList, 0 ,sizeof(hwndList)); 
  653.     nHWNDs = 0; 
  654.      
  655.     printtree(GetDesktopWindow(), &nHWNDs,
  656.         (char *) (strIndent + strlen(strIndent))); 
  657.  
  658.     winio_setpaint(__hMainWnd, TRUE); 
  659.     winio_home(__hMainWnd); 
  660.      
  661.     winio_setcurrent(hwndSave); 
  662.     } 
  663.  
  664.  
  665. // Note that the following subclassing WndProc uses _export. The 
  666. // combination of MakeProcInstance and _export is the only one that 
  667. // provides a correctly set up DS on entry across multiple instances. 
  668. // For a single instance, _loadds would do it, but a second instance 
  669. // will re-use the code, which will have a MOV AX, DGROUP in it that 
  670. // refers to the first instance. The MakeProcInstance thunk generates 
  671. // a MOV AX,hInstance; JMP FAR fn sequence. On entry to the function, 
  672. // the usual windows 3 byte prolog code is by default MOV AX, DS; NOP. 
  673. // The _export keyword causes the linker to smack in NOP; NOP; NOP over 
  674. // the top of those three bytes. The regular prolog then continues 
  675. // INC BP; PUSH BP; MOV BP, SP; PUSH DS; MOV DS, AX; It is only by 
  676. // using the _export keyword that the early part of the prolog will 
  677. // allow the MakeProcInstance thunk to make its effect 
  678. // known on the MOV DS, AX instruction at the end of the prolog! 
  679.  
  680. DWORD FAR PASCAL _export SnoopFunc(HWND hwnd, WORD wMsg, 
  681.                             WORD wParam, DWORD lParam) 
  682.     { 
  683.     int i, j; 
  684.     long lRet; 
  685.     LPSTR lpszTmp = NULL; 
  686.     WORD wTaskQSender, wThisTaskQ;
  687.     char szName[9];
  688.  
  689.     // See if this another instance checking on us... 
  690.     if (wMsg == wWM_SNOOPING) 
  691.         return TRUE; 
  692.      
  693.     // Locate info about this target window 
  694.     for (i = 0; snoopinfo[i].hwndTarget != hwnd; i++) ; 
  695.  
  696.     // Find message in the undocumented list 
  697.     for (j = 0; j < nUNDOCWMs; j++) 
  698.         if ((wMsg >= undocwm[j].wWMFrom) && 
  699.             (wMsg <= undocwm[j].wWMTo)) 
  700.             { 
  701.             if (lpszTmp = get_q_head(snoopinfo[i].hwndSnoop)) 
  702.                 {
  703.                 BOOL b1st = TRUE;
  704.                 // Queue message info for display
  705.                 // Note undocumented property of InSendMessage
  706.                 wsprintf(lpszTmp, "[%05u] %s(%04X) %04X %08lX %s%s", 
  707.                     ++(snoopinfo[i].wNum), undocwm[j].szWM, 
  708.                     wMsg, wParam, lParam, 
  709.                     (wTaskQSender = InSendMessage()) ? (LPSTR) ""
  710.                                             : (LPSTR) "Posted ", 
  711.                     (snoopinfo[i].bFilter)  ? (LPSTR) "- filtered" 
  712.                                     : (LPSTR) "");
  713.                 // Queue SendMessage history for display
  714.                 // Check we don't recurse (3.0 problem)
  715.                 wThisTaskQ = GetTaskQueue(GetCurrentTask());
  716.                 while (wTaskQSender && (wTaskQSender != wThisTaskQ))
  717.                     {
  718.                     // This line for 3.0 problem
  719.                     if (! verr(wTaskQSender)) break;
  720.                     if (lpszTmp = get_q_head(snoopinfo[i].hwndSnoop)) 
  721.                         {
  722.                         GetExeName(HINST_TASKQ(wTaskQSender), szName);
  723.                         wsprintf(lpszTmp, "\t %s%s%s",
  724.                             b1st ? (LPSTR) "Current task at SendMessage: "
  725.                                 : (LPSTR) "Before that: ",
  726.                             (LPSTR) &szName,
  727.                             (wTaskQSender == wThisTaskQ) ? (LPSTR) " etc.."
  728.                                                          : (LPSTR) "");
  729.                         b1st = FALSE;
  730.                         }
  731.                     wTaskQSender = SENDER_TO(wTaskQSender);
  732.                     }
  733.                 if (snoopinfo[i].bFilter) 
  734.                     goto bypass; 
  735.                 else 
  736.                 if (snoopinfo[i].bTrace) 
  737.                     { 
  738.                     hwndCurr = hwnd; 
  739.                     SetupInt3(); 
  740.                     } 
  741.                 } 
  742.             break; 
  743.             } 
  744.  
  745.     // Call original WndProc 
  746.     lRet = CallWindowProc(snoopinfo[i].oldWndProc, 
  747.                 hwnd, wMsg, wParam, lParam); 
  748.  
  749.     // Do we need to unplug 
  750.     if ((lpszTmp) && (snoopinfo[i].bTrace)) 
  751.         RestoreInt3(); 
  752.  
  753.     bypass: 
  754.      
  755.     // If message was WM_DESTROY, unhook ourselves 
  756.     if (wMsg == WM_DESTROY) 
  757.         { 
  758.         SetWindowLong(snoopinfo[i].hwndTarget, GWL_WNDPROC, 
  759.             (DWORD) snoopinfo[i].oldWndProc); 
  760.         snoopinfo[i].oldWndProc = NULL; 
  761.         if (lpszTmp = get_q_head(snoopinfo[i].hwndSnoop)) 
  762.             lstrcpy(lpszTmp, "**** Window closed ****"); 
  763.         } 
  764.      
  765.     return lRet; 
  766.     } 
  767.  
  768.  
  769.  
  770. // Unhook the subclassing WndProc  
  771. void stop_snooping(HWND hwnd) 
  772.     { 
  773.     int i; 
  774.      
  775.     // Locate info about window 
  776.     for (i = 0; snoopinfo[i].hwndSnoop != hwnd; i++) ; 
  777.  
  778.     // If we are still snooping, stop 
  779.     if (snoopinfo[i].oldWndProc) 
  780.         SetWindowLong(snoopinfo[i].hwndTarget, GWL_WNDPROC, 
  781.                         (DWORD) snoopinfo[i].oldWndProc); 
  782.     // Free up entry 
  783.     snoopinfo[i].oldWndProc = NULL; 
  784.     snoopinfo[i].hwndTarget = NULL; 
  785.     snoopinfo[i].hwndSnoop = NULL; 
  786.      
  787.     // The window hierarchy has surely changed - refresh it 
  788.     PostMessage(__hMainWnd, WM_COMMAND, ID_BUILDLIST, 0L); 
  789.     } 
  790.  
  791.  
  792.  
  793.  
  794. // Check that no other app has further subclassed the same window. 
  795. // If it has, unhooking ourselves in the regular way will leave 
  796. // the system unstable; the later subclassing app will hold the 
  797. // address of our WndProc which ceases to be valid upon our 
  798. // termination. 
  799. long check_close(HWND hwnd, WORD wMsg, WORD wParam, DWORD lParam) 
  800.     { 
  801.     int i; 
  802.      
  803.     for (i = 0; snoopinfo[i].hwndSnoop != hwnd; i++) ; 
  804.  
  805.     // Give user the option to avoid problems 
  806.     if ((! snoopinfo[i].oldWndProc) ||  
  807.         (GetWindowLong(snoopinfo[i].hwndTarget, GWL_WNDPROC) == 
  808.                     (LONG) lpfnSnoopFunc)) 
  809.         DestroyWindow(hwnd); 
  810.     else 
  811.         winio_warn(FALSE, szAppTitle, "CANNOT CLOSE WINDOW!\n\n" 
  812.             "Another application has subsequently\n" 
  813.             "subclassed window %04X. Closing this\n" 
  814.             "window would leave Windows in an unstable\n" 
  815.             "state. Close the application that is also\n" 
  816.             "monitoring window %04X messages, and retry.", 
  817.             snoopinfo[i].hwndTarget, snoopinfo[i].hwndTarget); 
  818.     return TRUE; 
  819.     } 
  820.  
  821.  
  822.  
  823. // Update the newly created snoop/monitoring window 
  824. void modify_snoopwindow(HWND hwnd, HMENU *phmenu) 
  825.     { 
  826.     // Add our menu options, and register handlers 
  827.     HMENU hMenuOptions = *phmenu = CreateMenu(); 
  828.     AppendMenu(hMenuOptions, MF_STRING | MF_ENABLED, 
  829.         ID_FILTER, "&Filter"); 
  830.     AppendMenu(hMenuOptions, MF_STRING | MF_ENABLED, 
  831.         ID_TRACE, "&Trace"); 
  832.      
  833.     AppendMenu(winio_hmenumain(hwnd), 
  834.         MF_STRING | MF_ENABLED, ID_CLEAR, "&Clear!"); 
  835.     AppendMenu(winio_hmenumain(hwnd), 
  836.         MF_STRING | MF_POPUP, hMenuOptions, "&Options"); 
  837.     DrawMenuBar(hwnd); 
  838.     winio_setmenufunc(hwnd, ID_CLEAR, (MENU_FUNC) doclear); 
  839.     winio_setmenufunc(hwnd, ID_FILTER, (MENU_FUNC) dofilter); 
  840.     winio_setmenufunc(hwnd, ID_TRACE, (MENU_FUNC) dotrace); 
  841.  
  842.     // Register the WM_DESTROY handler 
  843.     winio_onclose(hwnd, (DESTROY_FUNC) stop_snooping); 
  844.      
  845.     // Also add a WM_CLOSE handler to check for subsequent 
  846.     // WndProc chaining 
  847.     wmhandler_set(hwnd, WM_CLOSE, (WMHANDLER) check_close); 
  848.      
  849.     } 
  850.  
  851.  
  852.  
  853. // Double click handler from main window. When the user doubleclicks 
  854. // on a window entry in the main window hierarchy, this function 
  855. // installs the suclassing WndProc. 
  856. void start_snooping(HWND hwnd, LPSTR lpszLine, int nLine) 
  857.     { 
  858.     HWND hwndTgt; 
  859.     HWND hwndNew; 
  860.     char szTarget[100]; 
  861.     int i, iNew = -1; 
  862.  
  863.     // Get the corresponding HWND for the line clicked 
  864.     if (! (hwndTgt = hwndList[nLine - 1])) 
  865.         return; 
  866.      
  867.     // Check that the window still exists 
  868.     if (! IsWindow(hwndTgt)) 
  869.         { 
  870.         buildlist(hwnd, ID_BUILDLIST); 
  871.         error("Window no longer exists!"); 
  872.         return; 
  873.         } 
  874.      
  875.     // See if this is a duplicate. If not identify a free entry in 
  876.     // the info table. 
  877.     for (i = 0; i < MAX_WNDS; i++) 
  878.         if (snoopinfo[i].hwndTarget == hwndTgt) 
  879.             { 
  880.             winio_warn(FALSE, szAppTitle, 
  881.                 "Already snooping %04X", hwndTgt); 
  882.             return; 
  883.             } 
  884.         else 
  885.         if ((! snoopinfo[i].hwndTarget) && (iNew == -1)) 
  886.             iNew = i; 
  887.  
  888.     // See if another instance of SNOOP has hooked the window 
  889.     if (SendMessage(hwndTgt, wWM_SNOOPING, 0, 0L)) 
  890.         { 
  891.         winio_warn(FALSE, szAppTitle, 
  892.             "Another instance of SNOOP is\nalready snooping %04X", 
  893.             hwndTgt); 
  894.         return; 
  895.         } 
  896.          
  897.      
  898.     // Did we get a free entry 
  899.     if (iNew == -1) 
  900.         { 
  901.         error("All snoop slots busy...!"); 
  902.         return; 
  903.         } 
  904.  
  905.     // Strip leading spaces from the clicked line 
  906.     while (*lpszLine == ' ') lpszLine++; 
  907.     strcpy(szTarget, "Snooping "); 
  908.     lstrcat((LPSTR) &szTarget, lpszLine); 
  909.      
  910.     // Create a snooping window 
  911.     if (! (hwndNew = winio_window(szTarget, SNOOPBUF, WW_HASMENU))) 
  912.         { 
  913.         error("Snoop failed - No system resources!"); 
  914.         return; 
  915.         } 
  916.      
  917.     // set up the menu and handlers 
  918.     modify_snoopwindow(hwndNew, &snoopinfo[iNew].hMenuOpts); 
  919.      
  920.     // Initialize entry 
  921.     snoopinfo[iNew].wNum = 0; 
  922.     snoopinfo[iNew].bFilter = FALSE; 
  923.     snoopinfo[iNew].hwndTarget = hwndTgt; 
  924.     snoopinfo[iNew].hwndSnoop = hwndNew; 
  925.     snoopinfo[iNew].oldWndProc = (FARPROC) 
  926.         SetWindowLong(hwndTgt, GWL_WNDPROC, (DWORD) lpfnSnoopFunc); 
  927.     } 
  928.  
  929.  
  930.  
  931.  
  932. // Simple subsidiary help window triggered from the menu 
  933. void dohelp(HWND hwnd, int nID) 
  934.     {
  935.     HWND hSave;
  936.     static HWND hwndHelp = NULL;
  937.     
  938.     if (IsWindow(hwndHelp))
  939.         {
  940.         BringWindowToTop(hwndHelp);
  941.         return;
  942.         }
  943.     
  944.     if (! (hwndHelp = winio_window("HELP", 1, WW_HASMENU)))
  945.         return;
  946.     
  947.     hSave = winio_setcurrent(hwndHelp);
  948.     
  949.     winio_setpaint(hwndHelp, FALSE);
  950.     
  951.     printf(
  952.         "Usage is SNOOP [datfile]\n" 
  953.         "\twhere, if specified, datfile overrides the\n" 
  954.         "\tdefault WM_UNDOC.DAT.\n\n" 
  955.         "Double-click on any window entry in the main\n" 
  956.         "\tlist to start monitoring messages to\n" 
  957.         "\tthat window.\n" 
  958.         "Close the monitoring window to stop a\n" 
  959.         "\tmonitoring session\n" 
  960.         "The main list will be updated everytime\n" 
  961.         "\tone of the monitored windows closes.\n" 
  962.         "\tIn addition, you can use the Refresh\n" 
  963.         "\tmenu option to refresh the list at any\n" 
  964.         "\ttime.\n"
  965.         "Monitoring windows monitor messages\n"
  966.         "\tspecified in the file. They also show\n"
  967.         "\tthe chain of SendMessage calls that\n"
  968.         "\tpreceded the interception (unless the\n"
  969.         "message was posted).\n"
  970.         "Use the Options Filter menu selection to\n" 
  971.         "\tblock processing of messages to the\n" 
  972.         "\tmonitored window.\n" 
  973.         "Use the Options Trace menu selection to\n" 
  974.         "\tsee which default WndProc functions\n" 
  975.         "\thandle a listed message, together with\n" 
  976.         "\tany resultant messages generated:\n"
  977.         "\t  ->\tfunction entry\n"
  978.         "\t  <-\tfunction exit\n"
  979.         "\t  h\ttarget hwnd\n"
  980.         "\t  m\tmessage\n"
  981.         "\t  w\twparam\n"
  982.         "\t  l\tlparam\n"
  983.         "\t  ret\treturn value\n");
  984.  
  985.     winio_setpaint(hwndHelp, TRUE);
  986.     
  987.     winio_home(hwndHelp);
  988.     
  989.     winio_setcurrent(hSave);
  990.     } 
  991.  
  992.  
  993.  
  994. // Load, parse and store the specified file of undocumented 
  995. // descriptions. 
  996. void load_undoc_file(char *szFileName) 
  997.     { 
  998.     FILE *fileDat; 
  999.     WORD wWMFrom, wWMTo; 
  1000.     char szBuf[120]; 
  1001.     char szWM[100]; 
  1002.     char *sz; 
  1003.     int nTokens; 
  1004.      
  1005.     if (! (fileDat = fopen(szFileName, "rt"))) 
  1006.         fail("Cannot open %s", szFileName); 
  1007.  
  1008.     while (fgets(szBuf, sizeof(szBuf), fileDat)) 
  1009.         { 
  1010.         if (sz = strpbrk(szBuf, "/;")) *sz = 0; 
  1011.         nTokens = sscanf(szBuf, "%s %4X %4X", &szWM, &wWMFrom, &wWMTo); 
  1012.         if (nTokens < 2) continue; 
  1013.         if (nTokens == 2) wWMTo = wWMFrom; 
  1014.         undocwm[nUNDOCWMs].wWMFrom = wWMFrom; 
  1015.         undocwm[nUNDOCWMs].wWMTo = wWMTo; 
  1016.         if (! (undocwm[nUNDOCWMs].szWM = _fmalloc(strlen(szWM) + 1))) 
  1017.             { 
  1018.             puts("\n**** Insufficient memory"); 
  1019.             break; 
  1020.             } 
  1021.         lstrcpy(undocwm[nUNDOCWMs].szWM, (LPSTR) szWM); 
  1022.         nUNDOCWMs++; 
  1023.         } 
  1024.      
  1025.     fclose(fileDat); 
  1026.     } 
  1027.  
  1028.  
  1029.  
  1030.  
  1031. // Load, parse and store the specified file of documented 
  1032. // descriptions. 
  1033. void load_doc_file(char *szFileName) 
  1034.     { 
  1035.     FILE *fileDat; 
  1036.     WORD wWM; 
  1037.     char szBuf[120]; 
  1038.     char szWM[100]; 
  1039.     char *sz; 
  1040.     int nTokens; 
  1041.      
  1042.     if (! (fileDat = fopen(szFileName, "rt"))) 
  1043.         fail("Cannot open %s", szFileName); 
  1044.  
  1045.     while (fgets(szBuf, sizeof(szBuf), fileDat)) 
  1046.         { 
  1047.         if (sz = strpbrk(szBuf, "/;")) *sz = 0; 
  1048.         nTokens = sscanf(szBuf, "%s %4X", &szWM, &wWM); 
  1049.         if (nTokens < 2) continue; 
  1050.         docwm[nDOCWMs].wWM = wWM; 
  1051.         if (! (docwm[nDOCWMs].szWM = _fmalloc(strlen(szWM) + 1))) 
  1052.             { 
  1053.             puts("\n**** Insufficient memory"); 
  1054.             break; 
  1055.             } 
  1056.         lstrcpy(docwm[nDOCWMs].szWM, (LPSTR) szWM); 
  1057.         nDOCWMs++; 
  1058.         } 
  1059.      
  1060.     fclose(fileDat); 
  1061.     } 
  1062.  
  1063.  
  1064.  
  1065.  
  1066. // Build the main window's menubar 
  1067. void build_menu(void) 
  1068.     { 
  1069.     winio_about(
  1070.         "SNOOP"
  1071.         "\nWindows Message Tracing Utility"
  1072.         "\n\nCopyright (c) Dave Maxey, 1992"
  1073.         "\n\nFrom Chapter 4 of"
  1074.         "\n\"Undocumented Windows\" (Addison-Wesley, 1992)"
  1075.         "\nby Andrew Schulman, David Maxey and Matt Pietrek"
  1076.         );
  1077.     InsertMenu(winio_hmenumain(__hMainWnd), 1,
  1078.         MF_STRING | MF_ENABLED | MF_BYPOSITION, 
  1079.         ID_BUILDLIST, "&Refresh!"); 
  1080.     InsertMenu(winio_hmenuhelp(__hMainWnd), 0,
  1081.         MF_STRING | MF_ENABLED | MF_BYPOSITION, 
  1082.         ID_HELP, "&Usage"); 
  1083.     DrawMenuBar(__hMainWnd); 
  1084.     winio_setmenufunc(__hMainWnd, ID_BUILDLIST, (MENU_FUNC) buildlist); 
  1085.     winio_setmenufunc(__hMainWnd, ID_HELP, (MENU_FUNC) dohelp); 
  1086.     } 
  1087.  
  1088.  
  1089. void update_windows(void)
  1090.     {
  1091.     char *szQ;
  1092.     HWND hwndQ;
  1093.     int i;
  1094.     
  1095.     // This loop looks for, extracts, and displays on the 
  1096.     // appropriate monitoring window, messages queued up by the 
  1097.     // various hook and tracing functions.
  1098.     while (winio_openwindows())
  1099.         {
  1100.         wmhandler_yield();
  1101.         while (szQ = get_q_tail(&hwndQ)) 
  1102.             // Check that the snoop window hasn't been closed 
  1103.             for (i = 0; i < MAX_WNDS; i++) 
  1104.                 if (snoopinfo[i].hwndSnoop == hwndQ) 
  1105.                     { 
  1106.                     winio_setcurrent(hwndQ); 
  1107.                     puts(szQ); 
  1108.                     break; 
  1109.                     }
  1110.         }
  1111.     return 0;
  1112.     }
  1113.  
  1114.  
  1115. void closedown(void)
  1116.     {
  1117.     int i;
  1118.     
  1119.     // tidy up.... 
  1120.     for (i = 0; i < nDOCWMs; i++) 
  1121.         _ffree(docwm[i].szWM); 
  1122.  
  1123.     for (i = 0; i < nUNDOCWMs; i++) 
  1124.         _ffree(undocwm[i].szWM); 
  1125.  
  1126.     FreeProcInstance(lpfnSnoopFunc); 
  1127.     FreeProcInstance(lpfnInt3Handler); 
  1128.     // Regular FreeProcInstance works for our special thunk 
  1129.     FreeProcInstance(lpfnWndProcExit); 
  1130.  
  1131.     // Did we get any queue overflows? 
  1132.     if (cMissed) 
  1133.         winio_warn(FALSE, szAppTitle, "%ld messages not reported\n" 
  1134.             "due to queue overruns...", cMissed); 
  1135.     }
  1136.  
  1137.  
  1138. // MAIN 
  1139. int main(int argc, char *argv[]) 
  1140.     { 
  1141.     HWND hwndQ;
  1142.     int i; 
  1143.     FARPROC lpfnProc; 
  1144.     HANDLE hUser; 
  1145.     CLIENTCREATESTRUCT ccs; 
  1146.  
  1147.     b3_1 = HIBYTE(GetVersion());
  1148.     
  1149.     winio_settitle(__hMainWnd, szAppTitle); 
  1150.      
  1151.     // reset buffer size. 8k should be plenty, even in 3.0! 
  1152.     winio_setbufsize(__hMainWnd, 8192, FALSE); 
  1153.      
  1154.     // Get ourselves an inter-instance message 
  1155.     wWM_SNOOPING = RegisterWindowMessage(szWM_SNOOPING); 
  1156.  
  1157.     // Create regular procedure instances for subclassing WndProc 
  1158.     // and int 3 handler stub in I3HANDLR.ASM 
  1159.     lpfnSnoopFunc = 
  1160.         MakeProcInstance((FARPROC) SnoopFunc, __hInst); 
  1161.  
  1162.     lpfnInt3Handler = 
  1163.         MakeProcInstance((FARPROC) Int3Handler, __hInst); 
  1164.  
  1165.     // Create our own special procedure instance of the exit 
  1166.     // function stub in I3HANDLR.ASM 
  1167.     lpfnWndProcExit = 
  1168.         MakeProcInstanceBX((FARPROC) WndProcExit, __hInst); 
  1169.  
  1170.     hUser = GetModuleHandle("USER"); 
  1171.      
  1172.     // Collect entrypoint of all WndProcs that we will be 
  1173.     // monitoring in trace mode. 
  1174.     for (i = 0; i < MAX_PROCS; i++) 
  1175.         { 
  1176.         // If we can't get an entrypoint using GetProcAddress, 
  1177.         // see if we can get a built in class window WndProc. 
  1178.         if ((! (lpfnProc = (FARPROC) GetProcAddress(hUser, 
  1179.                     (LPSTR) procinfo[i].lpszProc))) && 
  1180.             (procinfo[i].lpszClass)) 
  1181.             { 
  1182.             hwndQ = CreateWindow( 
  1183.                     procinfo[i].lpszClass, (LPSTR) &szAppTitle, 0, 
  1184.                     CW_USEDEFAULT, CW_USEDEFAULT, 
  1185.                     CW_USEDEFAULT, CW_USEDEFAULT, 
  1186.                     __hMainWnd, NULL, __hInst, 
  1187.                     (LPSTR) &ccs); // just for MDICLIENT 
  1188.             lpfnProc = (FARPROC) GetWindowLong(hwndQ, GWL_WNDPROC); 
  1189.             DestroyWindow(hwndQ); 
  1190.             }
  1191.         else 
  1192.         if (procinfo[i].by1stCode)
  1193.             {
  1194.             lpfnProc =
  1195.                 (FARPROC) GetWindowLong(GetDesktopWindow(), GWL_WNDPROC);
  1196.             procinfo[i].by1stCode = 0;
  1197.             }
  1198.         // Did we manage to locate an entrypoint 
  1199.         if (! lpfnProc) 
  1200.             { 
  1201.             winio_warn(FALSE, szAppTitle, "%Fs not found!", 
  1202.                 procinfo[i].lpszProc); 
  1203.             return 0; 
  1204.             } 
  1205.      
  1206.         procinfo[i].lpfnProc = lpfnProc; 
  1207.         } 
  1208.  
  1209.     // Load command line specified, or default, message desc. file 
  1210.     load_undoc_file(argc > 1 ? strupr(argv[1]) : "WM_UNDOC.DAT"); 
  1211.      
  1212.     // Load file of all documented and undocumented messages 
  1213.     load_doc_file("WM_ALL.DAT"); 
  1214.      
  1215.     // Get USER.EXEs DGROUP/near heap selector for use in 
  1216.     // building the window hierarchy. 
  1217.     wSelUserDS = GetWindowWord(GetDesktopWindow(), GWW_HINSTANCE); 
  1218.  
  1219.     // Adjust the selector privilege level for 3.0 
  1220.     wSelUserDS = (wSelUserDS & 0xfffc) | 1;  
  1221.      
  1222.     // register the doubleclick handler 
  1223.     winio_setlinefn(__hMainWnd, (LINEHANDLER) start_snooping); 
  1224.  
  1225.     build_menu(); 
  1226.      
  1227.     memset(snoopinfo, 0, sizeof(snoopinfo)); 
  1228.      
  1229.     atexit(closedown);
  1230.     
  1231.     buildlist(__hMainWnd, ID_BUILDLIST); 
  1232.      
  1233.     update_windows();
  1234.     
  1235.     return 0; 
  1236.     } 
  1237.  
  1238.  
  1239.  
  1240.