home *** CD-ROM | disk | FTP | other *** search
- /*
- SNOOP.C -- Windows Message Interceptor and Trace Utility
-
- Copyright (c) Dave Maxey, 1992
-
- From Chapter 4 of "Undocumented Windows" (Addison-Wesley 1992)
- by Andrew Schulman, Dave Maxey and Matt Pietrek
-
- Build using: WINIOBC SNOOP I3HANDLR (for Borland C++ v3.00)
- WINIOMS SNOOP I3HANDLR (for Microsoft C/SDK)
- Note: Ensure that SNOOP.DEF is present
- */
-
- #include <windows.h>
- #include <wmhandlr.h>
- #include <winio.h>
- #include <io.h>
- #include <stdlib.h>
- #include <string.h>
- #include <malloc.h>
- #include <dos.h>
- #include "..\wnd.h"
-
- #ifndef __BORLANDC__
- #define MK_FP(a,b) ((void far *)(((unsigned long)(a) << 16) | (b)))
- #else
- #define asm _asm
- #endif
-
- #define ID_BUILDLIST 1
- #define ID_HELP 2
- #define ID_CLEAR 3
- #define ID_FILTER 4
- #define ID_TRACE 5
- #define MAX_UNDOCWMS 256
- #define MAX_DOCWMS 256
- #define MAX_WNDS 256
- #define MAX_QUEUE 100
- #define MAX_STACK 32
- #define SNOOPBUF 8192
- #define INT_3 0xCC
- #define MENUCLASS 0x8000
- #define DESKTOPCLASS 0x8001
- #define DIALOGCLASS 0x8002
- #define SWITCHWNDCLASS 0x8003
- #define ICONTITLECLASS 0x8004
-
-
- extern WORD FAR PASCAL SysErrorBox(LPSTR, LPSTR, WORD, WORD, WORD);
-
- BOOL b3_1 = TRUE; // needed for MACROs below
-
- /* MACROS to navigate the SendMessage history - See Chapter 5 */
-
- // Get the task queue handle of the task that sent 'h' its
- // current message. This info is held at offset 0x3A in the
- // TASKQUEUE structure. If not in a message, the field will
- // contain NULL.
- #ifdef HMMMM
- #define SENDER_TO(h) \
- (* (WORD FAR *) MK_FP((WORD) h, b3_1 ? 0x3A : 0x38))
- #else
- #define SENDER_TO(h) \
- (* (WORD FAR *) MK_FP((WORD) (h & 0xfffc) | 1, b3_1 ? 0x3A : 0x38))
- #endif
-
- // Get the hInstance of the task whose task
- // queue handle is 'h' by lifting the task database handle from
- // offset 2 of the task queue sructure, and then passing offset
- // 0x1C of the task database structure.
- #ifdef HMMMM
- #define HINST_TASKQ(h) \
- (* (HANDLE FAR *) MK_FP((* (HANDLE FAR *) MK_FP(h, 2)), 0x1C))
- #else
- #define HINST_TASKQ(h) \
- (* (HANDLE FAR *) MK_FP(((* (HANDLE FAR *) MK_FP((h & 0xfffc) | 1, 2)) & 0xfffc) | 1, 0x1C))
- #endif
-
-
- extern void FAR Int3Handler(void);
- extern void FAR WndProcExit(void);
- extern WORD FAR PASCAL AllocCStoDSAlias(WORD wSel);
- extern WORD FAR PASCAL AllocDStoCSAlias(WORD wSel);
- extern HANDLE FAR PASCAL GetTaskQueue(HANDLE hTask);
- extern HANDLE FAR PASCAL GetCurrentTask(void);
-
- typedef struct tagProcHook {
- LPSTR lpszProc;
- LPSTR lpszClass;
- FARPROC lpfnProc;
- BYTE by1stCode;
- } PROCINFO;
-
- typedef struct tagUndocWM {
- WORD wWMFrom;
- WORD wWMTo;
- LPSTR szWM;
- } UNDOCWM;
-
- typedef struct tagDocWM {
- WORD wWM;
- LPSTR szWM;
- } DOCWM;
-
- typedef struct tagSnoopInfo {
- HWND hwndTarget;
- HWND hwndSnoop;
- FARPROC oldWndProc;
- WORD wNum;
- HMENU hMenuOpts;
- BOOL bFilter;
- BOOL bTrace;
- } SNOOPINFO;
-
- typedef struct tagStackEntry {
- FARPROC fpCSIP;
- LPSTR lpszProc;
- HWND hwnd;
- LPSTR lpszMsg;
- DWORD lpStack;
- } STACKENTRY;
-
- typedef char q_entry[80];
-
- char szAppTitle[] = "Windows Message Snoop";
- char szWM_SNOOPING[] = "WM_SNOOPING";
- WORD wWM_SNOOPING;
- int nUNDOCWMs = 0;
- int nDOCWMs = 0;
- int nHWNDs;
-
- HWND hwndList[MAX_WNDS];
-
- SNOOPINFO snoopinfo[MAX_WNDS];
-
- UNDOCWM undocwm[MAX_UNDOCWMS];
-
- DOCWM docwm[MAX_DOCWMS];
-
- PROCINFO procinfo[] =
- /* WINDOW PROCEDURE CLASS ENTRY 1stBYTE */
- { "DefWindowProc", NULL, NULL, 0,
- "DefDlgProc", NULL, NULL, 0,
- "DefMDIChildProc", NULL, NULL, 0,
- "DefFrameProc", NULL, NULL, 0,
- "EditWndProc", "EDIT", NULL, 0,
- "ButtonWndProc", "BUTTON", NULL, 0,
- "ComboBoxCtlWndProc", "COMBOBOX", NULL, 0,
- "LBoxCtlWndProc", "LISTBOX", NULL, 0,
- "MDIClientWndProc", "MDICLIENT", NULL, 0,
- "SBWndProc", "SCROLLBAR", NULL, 0,
- "StaticWndProc", "STATIC", NULL, 0,
- "DesktopWndProc",(LPSTR) DESKTOPCLASS, NULL, 0,
- "MenuWndProc", (LPSTR) MENUCLASS, NULL, 0,
- };
- #define MAX_PROCS (sizeof(procinfo) / sizeof(PROCINFO))
-
- STACKENTRY stack[MAX_STACK] = {0};
- // int nSP = 0;
- //WORD wCounter = 1;
- int nIndent = 0;
- WORD wSelUserDS;
- FARPROC lpfnSnoopFunc;
- FARPROC lpfnInt3Handler;
- FARPROC lpfnWndProcExit;
- HWND qWin[MAX_QUEUE];
- q_entry szQueue[MAX_QUEUE];
- int q_head = 0, q_tail = 0;
- FARPROC ifOld = NULL;
- DWORD cMissed = 0L;
- HWND hwndCurr;
- char strIndent[MAX_STACK] = {0};
-
-
- // Display an error message box
- BOOL error(char *sz)
- {
- winio_warn(FALSE, szAppTitle, sz);
- return FALSE;
- }
-
-
- void GetExeName(HANDLE hInst, LPSTR strName)
- {
- static char str[128];
- int i;
-
- GetModuleFileName(hInst, str, 128);
- i = strlen(str) - 1;
-
- for ( ; i && (str[i] != '\\'); i--)
- if (str[i] == '.') str[i] = 0;
-
- if (str[i] == '\\') i++;
-
- // We cannot use strcpy in SMALL model - Assumes SS==DS
- lstrcpy(strName, (LPSTR) &str[i]);
- }
-
-
- void GetAnyAtomName(WORD wSeg, ATOM nAtom, LPSTR lpBuffer, int nSize)
- {
- if (! nAtom)
- {
- *lpBuffer = 0;
- return;
- }
- _asm push ds;
- _asm mov ds, word ptr wSeg;
- GetAtomName(nAtom, lpBuffer, nSize);
- _asm pop ds;
- }
-
-
- // Get a display queue entry to be fed to monitor window hwnd
- // or return NULL if the queue is full.
- LPSTR get_q_head(HWND hwnd)
- {
- int q_tmp = q_head;
-
- if (++q_head == MAX_QUEUE) q_head = 0;
- if (q_head == q_tail)
- {
- cMissed++;
- return NULL; // queue is full !
- }
- else
- {
- qWin[q_tmp] = hwnd;
- return (LPSTR) &szQueue[q_tmp];
- }
- }
-
-
- // Get an entry from the display queue, and the hwnd that
- // it is to be displayed upon.
- char *get_q_tail(HWND *phwnd)
- {
- int q_tmp = q_tail;
-
- if (q_tail == q_head) return NULL;
- if (++q_tail == MAX_QUEUE)
- q_tail = 0;
- *phwnd = qWin[q_tmp];
- return szQueue[q_tmp];
- }
-
-
-
- // Call DPMI to tell us where protected mode interrupt intno
- // currently points.
- FARPROC _dpmi_getvect(unsigned intno)
- {
- _asm {
- mov ax, 0204h
- mov bl, byte ptr intno
- int 31h
- /* return CX:DX in DX:AX */
- mov ax, dx
- mov dx, cx
- }
- }
-
-
- // Call DPMI to install our handler for protected mode interrupt
- // intno. Use the above function to check that it worked.
- BOOL _dpmi_setvect(unsigned intno, FARPROC handler)
- {
- _asm {
- mov ax, 0205h
- mov bl, byte ptr intno
- mov cx, word ptr handler+2
- mov dx, word ptr handler
- int 31h
- }
- return (_dpmi_getvect(intno) == handler);
- }
-
-
-
- WORD verr(WORD sel)
- {
- _asm mov ax, 1
- _asm verr word ptr sel
- _asm je short ok
- _asm dec ax
- ok:;
- }
-
-
-
- // This is a special version of MakeProcInstance. We use it to
- // create the procedure instance, or 'thunk', for the function
- // that will be called when any of the hooked default window
- // procedure functions return. The regular thunk sets the
- // instance's DGROUP selector value into AX ready for the
- // function prolog to MOV DS, AX. This would trash whatever
- // return code is being passed back from the hooked function.
- // Since our handler function had to be written in Assembler anyway,
- // we can use our own prolog code to take the DGROUP value from BX,
- // where our 'thunk' puts it.
- FARPROC MakeProcInstanceBX(FARPROC fp, HANDLE h)
- {
-
- #pragma pack(1)
- struct { BYTE mov_bx; WORD h; BYTE jmp_far; FARPROC fp; } *pthunk;
- #pragma pack()
-
- WORD hCS;
-
- if ((! (pthunk = malloc(sizeof(*pthunk)))) ||
- (! (hCS = AllocDStoCSAlias(h))))
- return NULL;
-
- pthunk->mov_bx = 0xBB;
- pthunk->h = h;
- pthunk->h &= 0xfffc;
- pthunk->h |= 1;
- pthunk->jmp_far = 0xEA;
- pthunk->fp = fp;
- return (FARPROC) MK_FP(hCS, (WORD) pthunk);
- }
-
-
- LPSTR MsgName(WORD wMsg)
- {
- int i;
-
- // If this is WM_USER + ??? return generic message
- if (wMsg >= WM_USER) return (LPSTR) "WM_USER+";
-
- // Look for message in the documented list first
- for (i = 0; i < nDOCWMs; i++)
- if (wMsg == docwm[i].wWM)
- return docwm[i].szWM;
-
- // Look for message in the undocumented list
- for (i = 0; i < nUNDOCWMs; i++)
- if ((wMsg >= undocwm[i].wWMFrom) &&
- (wMsg <= undocwm[i].wWMTo))
- return undocwm[i].szWM;
-
- return (LPSTR) "*WM_?*";
- }
-
-
-
- BYTE SmackCode(FARPROC lpfn, BYTE byInst)
- {
- BYTE byPrev;
- HANDLE hData, hDataRaw;
-
- // Ensure that the code hangs around for a second
- GetCodeHandle(lpfn);
-
- // And lock everything down
- GlobalPageLock(FP_SEG(lpfn));
-
- // Get a usable selector for code read/write
- hDataRaw = AllocCStoDSAlias(FP_SEG(lpfn));
-
- // Adjust the selector privilege level for 3.0
- hData = (hDataRaw & 0xfffc) | 1;
-
- // Trade in the new instruction
- byPrev = *((LPBYTE) MK_FP(hData, FP_OFF(lpfn)));
- *((LPBYTE) MK_FP(hData, FP_OFF(lpfn))) = byInst;
-
- // Unlock the code
- GlobalUnlock(FP_SEG(lpfn));
-
- // Free the alias selector
- FreeSelector(hDataRaw);
-
- return byPrev;
- }
-
-
- // The following procedure must be cdecl. On the stack is the return
- // code from the DefXXXXXProc that has returned to us. We must return
- // the return address that was there before we hooked ourselves in...
- FARPROC WndProcExitProc( DWORD lpStack, WORD hCallerDS, DWORD lRet)
- {
- int j, i;
- LPSTR lpszTmp;
-
- for (i = 0; i < MAX_STACK; i++)
- if ((lpStack == stack[i].lpStack) ||
- ((lpStack + 2) == stack[i].lpStack)) break;
-
- if (i == MAX_STACK)
- SysErrorBox("Unmatched return - System will now crash!",
- szAppTitle, 0, 0x8001, 0);
-
- // if (! nSP--)
- // SysErrorBox("Stack underflow - System will now crash!",
- // szAppTitle, 0, 0x8001, 0);
- // if (lpStack != stack[i].lpStack)
- // SysErrorBox("Stack mismatch - System will now crash!",
- // szAppTitle, 0, 0x8001, 0);
-
- // SysErrorBox("Return", stack[nSP].lpszProc, 0, 1, 0);
-
- // Try and reinstate Int 3 for all hooks
- for (j = 0; j < MAX_PROCS; j++)
- if (! procinfo[j].by1stCode)
- procinfo[j].by1stCode = SmackCode(procinfo[j].lpfnProc, INT_3);
-
- // Locate info about this target window
- for (j = 0; j < MAX_WNDS; j++)
- if (snoopinfo[j].hwndTarget == hwndCurr)
- break;
-
- // Record the return in the display queue
- strIndent[--nIndent] = 0;
- if ((j != MAX_WNDS) &&
- (lpszTmp = get_q_head(snoopinfo[j].hwndSnoop)))
- wsprintf(lpszTmp, " %s<-%s ret %08lX (h=%04X m=%s)",
- (LPSTR) strIndent, stack[i].lpszProc, lRet,
- stack[i].hwnd, stack[i].lpszMsg);
-
- stack[i].lpStack = NULL;
- return stack[i].fpCSIP;
- }
-
-
-
- // The following procedure must be cdecl.
- void Int3EntryProc(DWORD lpStack, WORD wCallerDS, FARPROC fpWndProc,
- WORD wFlags, FARPROC fpCallerCSIP,
- DWORD lParam, WORD wParam, WORD wMsg, HWND hwnd)
-
- {
- LPSTR lpszTmp, lpszMsg;
- int i, j, k;
-
- for (i = 0; i < MAX_PROCS; i++)
- if (fpWndProc == procinfo[i].lpfnProc)
- break;
-
- if (i == MAX_PROCS)
- SysErrorBox("Unrecognized INT 3 - Crash coming!",
- szAppTitle, 0, 0x8001, 0);
-
- // SysErrorBox("Entry", procinfo[i].lpszProc, 0, 1, 0);
-
- for (k = 0; k < MAX_STACK; k++)
- if (stack[k].lpStack == NULL) break;
-
- // Save away on our save 'stack' return address for exit proc
- // and smack exit proc onto stack in its place
- if (k == MAX_STACK)
- SysErrorBox("Stack overrun - The system will now crash!",
- szAppTitle, 0, 0x8001, 0);
-
- // Reinstate instruction in place of int 3 for current hook,
- // and ensure int 3s in place for all other hooks
- for (j = 0; j < MAX_PROCS; j++)
- {
- if (j == i)
- {
- if (procinfo[j].by1stCode)
- {
- SmackCode(procinfo[j].lpfnProc, procinfo[j].by1stCode);
- procinfo[j].by1stCode = 0;
- }
- }
- else
- if (! procinfo[j].by1stCode)
- procinfo[j].by1stCode = SmackCode(procinfo[j].lpfnProc, INT_3);
- }
-
- // Locate info about this target window
- for (j = 0; j < MAX_WNDS; j++)
- if (snoopinfo[j].hwndTarget == hwndCurr)
- break;
-
- // Get message WM_ name
- lpszMsg = MsgName(wMsg);
-
- // Record entry in the display queue
- if ((j != MAX_WNDS) &&
- (lpszTmp = get_q_head(snoopinfo[j].hwndSnoop)))
- wsprintf(lpszTmp, " %s->%s h=%04X m=%s(%04X) w=%04X l=%08lX",
- (LPSTR) strIndent, procinfo[i].lpszProc,
- hwnd, lpszMsg, wMsg, wParam, lParam);
-
- // Store information we will need at hooked function return time
- strIndent[nIndent++] = ' ';
- stack[k].lpszProc = procinfo[i].lpszProc;
- stack[k].fpCSIP = fpCallerCSIP;
- stack[k].lpszMsg = lpszMsg;
- stack[k].hwnd = hwnd;
- stack[k].lpStack = lpStack;
-
- // Hook our function in as return address for hooked function
- fpCallerCSIP = lpfnWndProcExit;
- }
-
-
-
- // Int 3 is how tracing is accomplished if the 'trace' option is
- // selected. First job when a listed message is intercepted in trace
- // mode is to establish our Int 3 handler, and set int 3s into all
- // default window function entrypoints. Note that it would be a problem
- // if we installed an int 3 handler for a second or subsequent
- // time, as we would overwrite the saved vector with our own handler
- // address.
- void SetupInt3(void)
- {
- int i;
-
- if (! ifOld)
- {
- ifOld = _dpmi_getvect(3);
- if (! _dpmi_setvect(3, lpfnInt3Handler))
- {
- error("Could not hook Int 3!");
- return;
- }
- }
-
- for (i = 0; i < MAX_PROCS; i++)
- if (! procinfo[i].by1stCode)
- procinfo[i].by1stCode = SmackCode(procinfo[i].lpfnProc, INT_3);
- }
-
-
- // Unhook all int 3s, reinstating original instructions. Then
- // restore previous int 3 handler.
- void RestoreInt3(void)
- {
- int i;
-
-
- for (i = 0; i < MAX_PROCS; i++)
- if (procinfo[i].by1stCode)
- {
- SmackCode(procinfo[i].lpfnProc, procinfo[i].by1stCode);
- procinfo[i].by1stCode = 0;
- }
-
- if (ifOld)
- {
- if (! _dpmi_setvect(3, ifOld))
- error("Could not restore Int 3!");
- ifOld = NULL;
- }
- }
-
-
- // Display a window hierarchy on the main window. Calls itself
- // recursively for child windows.
- void printtree(WORD wndofs, int *pi, char *strIndent)
- {
- WND_3_0 FAR *hwnd30;
- WND_3_1 FAR *hwnd31;
- CLASS FAR *pcls;
- WORD clsofs;
- char strText[128];
- char strClass[30];
- int i;
-
- i = *pi;
-
- for (;;)
- {
- // add to the global list of HWNDs
- hwndList[i++] = wndofs;
- (void far *) hwnd31 = (void far *) hwnd30 = MK_FP(wSelUserDS, wndofs);
- clsofs = b3_1 ? (WORD) (hwnd31->hClass) : (WORD) (hwnd30->hClass);
- (void far *) pcls = MK_FP(wSelUserDS, clsofs);
- if (pcls->wSig != 0x4b4e)
- winio_warn(FALSE, __szModule, "Bad class pointer");
- GetWindowText((HWND) wndofs,
- strText, sizeof(strText));
- GetAnyAtomName(wSelUserDS, pcls->atomCls,
- (LPSTR) &strClass, sizeof(strClass));
- if (strClass[0] == '#')
- GlobalGetAtomName(pcls->atomCls /* & 0x7fff */,
- (LPSTR) &strClass, sizeof(strClass));
- printf("%s%04X %s (%s)\n", strIndent, wndofs, strText, strClass);
- if (hwnd30->hwndChild != NULL)
- {
- strIndent -= 4;
- printtree(hwnd30->hwndChild, &i, strIndent);
- strIndent += 4;
- }
-
- if ((wndofs = hwnd30->hwndNext) == NULL)
- break;
- }
- *pi = i;
- }
-
-
-
- // Reset a monitoring window's history
- void doclear(HWND hwnd, int nID)
- {
- int i;
-
- // Locate info about window
- for (i = 0; snoopinfo[i].hwndSnoop != hwnd; i++) ;
-
- snoopinfo[i].wNum = 0;
- winio_clear(hwnd);
- }
-
-
-
- // Toggle the 'filter' menu option.
- void dofilter(HWND hwnd, int nID)
- {
- int i;
-
- // Locate info about window
- for (i = 0; snoopinfo[i].hwndSnoop != hwnd; i++) ;
-
- CheckMenuItem(snoopinfo[i].hMenuOpts, ID_FILTER,
- snoopinfo[i].bFilter ^= -1 ? MF_CHECKED : MF_UNCHECKED);
- }
-
-
-
- // Toggle the 'trace' menu option.
- void dotrace(HWND hwnd, int nID)
- {
- int i;
-
- // Locate info about window
- for (i = 0; snoopinfo[i].hwndSnoop != hwnd; i++) ;
-
- CheckMenuItem(snoopinfo[i].hMenuOpts, ID_TRACE,
- snoopinfo[i].bTrace ^= -1 ? MF_CHECKED : MF_UNCHECKED);
- }
-
-
-
- // Respond to a request to build the window handle tree
- void buildlist(HWND hwnd, int nID)
- {
- HWND hwndSave = winio_setcurrent(__hMainWnd);
- char *strIndent = " ";
-
- winio_clear(__hMainWnd);
- winio_setpaint(__hMainWnd, FALSE);
-
-
-
- // Initialize the global llist of HWNDs
- memset(hwndList, 0 ,sizeof(hwndList));
- nHWNDs = 0;
-
- printtree(GetDesktopWindow(), &nHWNDs,
- (char *) (strIndent + strlen(strIndent)));
-
- winio_setpaint(__hMainWnd, TRUE);
- winio_home(__hMainWnd);
-
- winio_setcurrent(hwndSave);
- }
-
-
- // Note that the following subclassing WndProc uses _export. The
- // combination of MakeProcInstance and _export is the only one that
- // provides a correctly set up DS on entry across multiple instances.
- // For a single instance, _loadds would do it, but a second instance
- // will re-use the code, which will have a MOV AX, DGROUP in it that
- // refers to the first instance. The MakeProcInstance thunk generates
- // a MOV AX,hInstance; JMP FAR fn sequence. On entry to the function,
- // the usual windows 3 byte prolog code is by default MOV AX, DS; NOP.
- // The _export keyword causes the linker to smack in NOP; NOP; NOP over
- // the top of those three bytes. The regular prolog then continues
- // INC BP; PUSH BP; MOV BP, SP; PUSH DS; MOV DS, AX; It is only by
- // using the _export keyword that the early part of the prolog will
- // allow the MakeProcInstance thunk to make its effect
- // known on the MOV DS, AX instruction at the end of the prolog!
-
- DWORD FAR PASCAL _export SnoopFunc(HWND hwnd, WORD wMsg,
- WORD wParam, DWORD lParam)
- {
- int i, j;
- long lRet;
- LPSTR lpszTmp = NULL;
- WORD wTaskQSender, wThisTaskQ;
- char szName[9];
-
- // See if this another instance checking on us...
- if (wMsg == wWM_SNOOPING)
- return TRUE;
-
- // Locate info about this target window
- for (i = 0; snoopinfo[i].hwndTarget != hwnd; i++) ;
-
- // Find message in the undocumented list
- for (j = 0; j < nUNDOCWMs; j++)
- if ((wMsg >= undocwm[j].wWMFrom) &&
- (wMsg <= undocwm[j].wWMTo))
- {
- if (lpszTmp = get_q_head(snoopinfo[i].hwndSnoop))
- {
- BOOL b1st = TRUE;
- // Queue message info for display
- // Note undocumented property of InSendMessage
- wsprintf(lpszTmp, "[%05u] %s(%04X) %04X %08lX %s%s",
- ++(snoopinfo[i].wNum), undocwm[j].szWM,
- wMsg, wParam, lParam,
- (wTaskQSender = InSendMessage()) ? (LPSTR) ""
- : (LPSTR) "Posted ",
- (snoopinfo[i].bFilter) ? (LPSTR) "- filtered"
- : (LPSTR) "");
- // Queue SendMessage history for display
- // Check we don't recurse (3.0 problem)
- wThisTaskQ = GetTaskQueue(GetCurrentTask());
- while (wTaskQSender && (wTaskQSender != wThisTaskQ))
- {
- // This line for 3.0 problem
- if (! verr(wTaskQSender)) break;
- if (lpszTmp = get_q_head(snoopinfo[i].hwndSnoop))
- {
- GetExeName(HINST_TASKQ(wTaskQSender), szName);
- wsprintf(lpszTmp, "\t %s%s%s",
- b1st ? (LPSTR) "Current task at SendMessage: "
- : (LPSTR) "Before that: ",
- (LPSTR) &szName,
- (wTaskQSender == wThisTaskQ) ? (LPSTR) " etc.."
- : (LPSTR) "");
- b1st = FALSE;
- }
- wTaskQSender = SENDER_TO(wTaskQSender);
- }
- if (snoopinfo[i].bFilter)
- goto bypass;
- else
- if (snoopinfo[i].bTrace)
- {
- hwndCurr = hwnd;
- SetupInt3();
- }
- }
- break;
- }
-
- // Call original WndProc
- lRet = CallWindowProc(snoopinfo[i].oldWndProc,
- hwnd, wMsg, wParam, lParam);
-
- // Do we need to unplug
- if ((lpszTmp) && (snoopinfo[i].bTrace))
- RestoreInt3();
-
- bypass:
-
- // If message was WM_DESTROY, unhook ourselves
- if (wMsg == WM_DESTROY)
- {
- SetWindowLong(snoopinfo[i].hwndTarget, GWL_WNDPROC,
- (DWORD) snoopinfo[i].oldWndProc);
- snoopinfo[i].oldWndProc = NULL;
- if (lpszTmp = get_q_head(snoopinfo[i].hwndSnoop))
- lstrcpy(lpszTmp, "**** Window closed ****");
- }
-
- return lRet;
- }
-
-
-
- // Unhook the subclassing WndProc
- void stop_snooping(HWND hwnd)
- {
- int i;
-
- // Locate info about window
- for (i = 0; snoopinfo[i].hwndSnoop != hwnd; i++) ;
-
- // If we are still snooping, stop
- if (snoopinfo[i].oldWndProc)
- SetWindowLong(snoopinfo[i].hwndTarget, GWL_WNDPROC,
- (DWORD) snoopinfo[i].oldWndProc);
- // Free up entry
- snoopinfo[i].oldWndProc = NULL;
- snoopinfo[i].hwndTarget = NULL;
- snoopinfo[i].hwndSnoop = NULL;
-
- // The window hierarchy has surely changed - refresh it
- PostMessage(__hMainWnd, WM_COMMAND, ID_BUILDLIST, 0L);
- }
-
-
-
-
- // Check that no other app has further subclassed the same window.
- // If it has, unhooking ourselves in the regular way will leave
- // the system unstable; the later subclassing app will hold the
- // address of our WndProc which ceases to be valid upon our
- // termination.
- long check_close(HWND hwnd, WORD wMsg, WORD wParam, DWORD lParam)
- {
- int i;
-
- for (i = 0; snoopinfo[i].hwndSnoop != hwnd; i++) ;
-
- // Give user the option to avoid problems
- if ((! snoopinfo[i].oldWndProc) ||
- (GetWindowLong(snoopinfo[i].hwndTarget, GWL_WNDPROC) ==
- (LONG) lpfnSnoopFunc))
- DestroyWindow(hwnd);
- else
- winio_warn(FALSE, szAppTitle, "CANNOT CLOSE WINDOW!\n\n"
- "Another application has subsequently\n"
- "subclassed window %04X. Closing this\n"
- "window would leave Windows in an unstable\n"
- "state. Close the application that is also\n"
- "monitoring window %04X messages, and retry.",
- snoopinfo[i].hwndTarget, snoopinfo[i].hwndTarget);
- return TRUE;
- }
-
-
-
- // Update the newly created snoop/monitoring window
- void modify_snoopwindow(HWND hwnd, HMENU *phmenu)
- {
- // Add our menu options, and register handlers
- HMENU hMenuOptions = *phmenu = CreateMenu();
- AppendMenu(hMenuOptions, MF_STRING | MF_ENABLED,
- ID_FILTER, "&Filter");
- AppendMenu(hMenuOptions, MF_STRING | MF_ENABLED,
- ID_TRACE, "&Trace");
-
- AppendMenu(winio_hmenumain(hwnd),
- MF_STRING | MF_ENABLED, ID_CLEAR, "&Clear!");
- AppendMenu(winio_hmenumain(hwnd),
- MF_STRING | MF_POPUP, hMenuOptions, "&Options");
- DrawMenuBar(hwnd);
- winio_setmenufunc(hwnd, ID_CLEAR, (MENU_FUNC) doclear);
- winio_setmenufunc(hwnd, ID_FILTER, (MENU_FUNC) dofilter);
- winio_setmenufunc(hwnd, ID_TRACE, (MENU_FUNC) dotrace);
-
- // Register the WM_DESTROY handler
- winio_onclose(hwnd, (DESTROY_FUNC) stop_snooping);
-
- // Also add a WM_CLOSE handler to check for subsequent
- // WndProc chaining
- wmhandler_set(hwnd, WM_CLOSE, (WMHANDLER) check_close);
-
- }
-
-
-
- // Double click handler from main window. When the user doubleclicks
- // on a window entry in the main window hierarchy, this function
- // installs the suclassing WndProc.
- void start_snooping(HWND hwnd, LPSTR lpszLine, int nLine)
- {
- HWND hwndTgt;
- HWND hwndNew;
- char szTarget[100];
- int i, iNew = -1;
-
- // Get the corresponding HWND for the line clicked
- if (! (hwndTgt = hwndList[nLine - 1]))
- return;
-
- // Check that the window still exists
- if (! IsWindow(hwndTgt))
- {
- buildlist(hwnd, ID_BUILDLIST);
- error("Window no longer exists!");
- return;
- }
-
- // See if this is a duplicate. If not identify a free entry in
- // the info table.
- for (i = 0; i < MAX_WNDS; i++)
- if (snoopinfo[i].hwndTarget == hwndTgt)
- {
- winio_warn(FALSE, szAppTitle,
- "Already snooping %04X", hwndTgt);
- return;
- }
- else
- if ((! snoopinfo[i].hwndTarget) && (iNew == -1))
- iNew = i;
-
- // See if another instance of SNOOP has hooked the window
- if (SendMessage(hwndTgt, wWM_SNOOPING, 0, 0L))
- {
- winio_warn(FALSE, szAppTitle,
- "Another instance of SNOOP is\nalready snooping %04X",
- hwndTgt);
- return;
- }
-
-
- // Did we get a free entry
- if (iNew == -1)
- {
- error("All snoop slots busy...!");
- return;
- }
-
- // Strip leading spaces from the clicked line
- while (*lpszLine == ' ') lpszLine++;
- strcpy(szTarget, "Snooping ");
- lstrcat((LPSTR) &szTarget, lpszLine);
-
- // Create a snooping window
- if (! (hwndNew = winio_window(szTarget, SNOOPBUF, WW_HASMENU)))
- {
- error("Snoop failed - No system resources!");
- return;
- }
-
- // set up the menu and handlers
- modify_snoopwindow(hwndNew, &snoopinfo[iNew].hMenuOpts);
-
- // Initialize entry
- snoopinfo[iNew].wNum = 0;
- snoopinfo[iNew].bFilter = FALSE;
- snoopinfo[iNew].hwndTarget = hwndTgt;
- snoopinfo[iNew].hwndSnoop = hwndNew;
- snoopinfo[iNew].oldWndProc = (FARPROC)
- SetWindowLong(hwndTgt, GWL_WNDPROC, (DWORD) lpfnSnoopFunc);
- }
-
-
-
-
- // Simple subsidiary help window triggered from the menu
- void dohelp(HWND hwnd, int nID)
- {
- HWND hSave;
- static HWND hwndHelp = NULL;
-
- if (IsWindow(hwndHelp))
- {
- BringWindowToTop(hwndHelp);
- return;
- }
-
- if (! (hwndHelp = winio_window("HELP", 1, WW_HASMENU)))
- return;
-
- hSave = winio_setcurrent(hwndHelp);
-
- winio_setpaint(hwndHelp, FALSE);
-
- printf(
- "Usage is SNOOP [datfile]\n"
- "\twhere, if specified, datfile overrides the\n"
- "\tdefault WM_UNDOC.DAT.\n\n"
- "Double-click on any window entry in the main\n"
- "\tlist to start monitoring messages to\n"
- "\tthat window.\n"
- "Close the monitoring window to stop a\n"
- "\tmonitoring session\n"
- "The main list will be updated everytime\n"
- "\tone of the monitored windows closes.\n"
- "\tIn addition, you can use the Refresh\n"
- "\tmenu option to refresh the list at any\n"
- "\ttime.\n"
- "Monitoring windows monitor messages\n"
- "\tspecified in the file. They also show\n"
- "\tthe chain of SendMessage calls that\n"
- "\tpreceded the interception (unless the\n"
- "message was posted).\n"
- "Use the Options Filter menu selection to\n"
- "\tblock processing of messages to the\n"
- "\tmonitored window.\n"
- "Use the Options Trace menu selection to\n"
- "\tsee which default WndProc functions\n"
- "\thandle a listed message, together with\n"
- "\tany resultant messages generated:\n"
- "\t ->\tfunction entry\n"
- "\t <-\tfunction exit\n"
- "\t h\ttarget hwnd\n"
- "\t m\tmessage\n"
- "\t w\twparam\n"
- "\t l\tlparam\n"
- "\t ret\treturn value\n");
-
- winio_setpaint(hwndHelp, TRUE);
-
- winio_home(hwndHelp);
-
- winio_setcurrent(hSave);
- }
-
-
-
- // Load, parse and store the specified file of undocumented
- // descriptions.
- void load_undoc_file(char *szFileName)
- {
- FILE *fileDat;
- WORD wWMFrom, wWMTo;
- char szBuf[120];
- char szWM[100];
- char *sz;
- int nTokens;
-
- if (! (fileDat = fopen(szFileName, "rt")))
- fail("Cannot open %s", szFileName);
-
- while (fgets(szBuf, sizeof(szBuf), fileDat))
- {
- if (sz = strpbrk(szBuf, "/;")) *sz = 0;
- nTokens = sscanf(szBuf, "%s %4X %4X", &szWM, &wWMFrom, &wWMTo);
- if (nTokens < 2) continue;
- if (nTokens == 2) wWMTo = wWMFrom;
- undocwm[nUNDOCWMs].wWMFrom = wWMFrom;
- undocwm[nUNDOCWMs].wWMTo = wWMTo;
- if (! (undocwm[nUNDOCWMs].szWM = _fmalloc(strlen(szWM) + 1)))
- {
- puts("\n**** Insufficient memory");
- break;
- }
- lstrcpy(undocwm[nUNDOCWMs].szWM, (LPSTR) szWM);
- nUNDOCWMs++;
- }
-
- fclose(fileDat);
- }
-
-
-
-
- // Load, parse and store the specified file of documented
- // descriptions.
- void load_doc_file(char *szFileName)
- {
- FILE *fileDat;
- WORD wWM;
- char szBuf[120];
- char szWM[100];
- char *sz;
- int nTokens;
-
- if (! (fileDat = fopen(szFileName, "rt")))
- fail("Cannot open %s", szFileName);
-
- while (fgets(szBuf, sizeof(szBuf), fileDat))
- {
- if (sz = strpbrk(szBuf, "/;")) *sz = 0;
- nTokens = sscanf(szBuf, "%s %4X", &szWM, &wWM);
- if (nTokens < 2) continue;
- docwm[nDOCWMs].wWM = wWM;
- if (! (docwm[nDOCWMs].szWM = _fmalloc(strlen(szWM) + 1)))
- {
- puts("\n**** Insufficient memory");
- break;
- }
- lstrcpy(docwm[nDOCWMs].szWM, (LPSTR) szWM);
- nDOCWMs++;
- }
-
- fclose(fileDat);
- }
-
-
-
-
- // Build the main window's menubar
- void build_menu(void)
- {
- winio_about(
- "SNOOP"
- "\nWindows Message Tracing Utility"
- "\n\nCopyright (c) Dave Maxey, 1992"
- "\n\nFrom Chapter 4 of"
- "\n\"Undocumented Windows\" (Addison-Wesley, 1992)"
- "\nby Andrew Schulman, David Maxey and Matt Pietrek"
- );
- InsertMenu(winio_hmenumain(__hMainWnd), 1,
- MF_STRING | MF_ENABLED | MF_BYPOSITION,
- ID_BUILDLIST, "&Refresh!");
- InsertMenu(winio_hmenuhelp(__hMainWnd), 0,
- MF_STRING | MF_ENABLED | MF_BYPOSITION,
- ID_HELP, "&Usage");
- DrawMenuBar(__hMainWnd);
- winio_setmenufunc(__hMainWnd, ID_BUILDLIST, (MENU_FUNC) buildlist);
- winio_setmenufunc(__hMainWnd, ID_HELP, (MENU_FUNC) dohelp);
- }
-
-
- void update_windows(void)
- {
- char *szQ;
- HWND hwndQ;
- int i;
-
- // This loop looks for, extracts, and displays on the
- // appropriate monitoring window, messages queued up by the
- // various hook and tracing functions.
- while (winio_openwindows())
- {
- wmhandler_yield();
- while (szQ = get_q_tail(&hwndQ))
- // Check that the snoop window hasn't been closed
- for (i = 0; i < MAX_WNDS; i++)
- if (snoopinfo[i].hwndSnoop == hwndQ)
- {
- winio_setcurrent(hwndQ);
- puts(szQ);
- break;
- }
- }
- return 0;
- }
-
-
- void closedown(void)
- {
- int i;
-
- // tidy up....
- for (i = 0; i < nDOCWMs; i++)
- _ffree(docwm[i].szWM);
-
- for (i = 0; i < nUNDOCWMs; i++)
- _ffree(undocwm[i].szWM);
-
- FreeProcInstance(lpfnSnoopFunc);
- FreeProcInstance(lpfnInt3Handler);
- // Regular FreeProcInstance works for our special thunk
- FreeProcInstance(lpfnWndProcExit);
-
- // Did we get any queue overflows?
- if (cMissed)
- winio_warn(FALSE, szAppTitle, "%ld messages not reported\n"
- "due to queue overruns...", cMissed);
- }
-
-
- // MAIN
- int main(int argc, char *argv[])
- {
- HWND hwndQ;
- int i;
- FARPROC lpfnProc;
- HANDLE hUser;
- CLIENTCREATESTRUCT ccs;
-
- b3_1 = HIBYTE(GetVersion());
-
- winio_settitle(__hMainWnd, szAppTitle);
-
- // reset buffer size. 8k should be plenty, even in 3.0!
- winio_setbufsize(__hMainWnd, 8192, FALSE);
-
- // Get ourselves an inter-instance message
- wWM_SNOOPING = RegisterWindowMessage(szWM_SNOOPING);
-
- // Create regular procedure instances for subclassing WndProc
- // and int 3 handler stub in I3HANDLR.ASM
- lpfnSnoopFunc =
- MakeProcInstance((FARPROC) SnoopFunc, __hInst);
-
- lpfnInt3Handler =
- MakeProcInstance((FARPROC) Int3Handler, __hInst);
-
- // Create our own special procedure instance of the exit
- // function stub in I3HANDLR.ASM
- lpfnWndProcExit =
- MakeProcInstanceBX((FARPROC) WndProcExit, __hInst);
-
- hUser = GetModuleHandle("USER");
-
- // Collect entrypoint of all WndProcs that we will be
- // monitoring in trace mode.
- for (i = 0; i < MAX_PROCS; i++)
- {
- // If we can't get an entrypoint using GetProcAddress,
- // see if we can get a built in class window WndProc.
- if ((! (lpfnProc = (FARPROC) GetProcAddress(hUser,
- (LPSTR) procinfo[i].lpszProc))) &&
- (procinfo[i].lpszClass))
- {
- hwndQ = CreateWindow(
- procinfo[i].lpszClass, (LPSTR) &szAppTitle, 0,
- CW_USEDEFAULT, CW_USEDEFAULT,
- CW_USEDEFAULT, CW_USEDEFAULT,
- __hMainWnd, NULL, __hInst,
- (LPSTR) &ccs); // just for MDICLIENT
- lpfnProc = (FARPROC) GetWindowLong(hwndQ, GWL_WNDPROC);
- DestroyWindow(hwndQ);
- }
- else
- if (procinfo[i].by1stCode)
- {
- lpfnProc =
- (FARPROC) GetWindowLong(GetDesktopWindow(), GWL_WNDPROC);
- procinfo[i].by1stCode = 0;
- }
- // Did we manage to locate an entrypoint
- if (! lpfnProc)
- {
- winio_warn(FALSE, szAppTitle, "%Fs not found!",
- procinfo[i].lpszProc);
- return 0;
- }
-
- procinfo[i].lpfnProc = lpfnProc;
- }
-
- // Load command line specified, or default, message desc. file
- load_undoc_file(argc > 1 ? strupr(argv[1]) : "WM_UNDOC.DAT");
-
- // Load file of all documented and undocumented messages
- load_doc_file("WM_ALL.DAT");
-
- // Get USER.EXEs DGROUP/near heap selector for use in
- // building the window hierarchy.
- wSelUserDS = GetWindowWord(GetDesktopWindow(), GWW_HINSTANCE);
-
- // Adjust the selector privilege level for 3.0
- wSelUserDS = (wSelUserDS & 0xfffc) | 1;
-
- // register the doubleclick handler
- winio_setlinefn(__hMainWnd, (LINEHANDLER) start_snooping);
-
- build_menu();
-
- memset(snoopinfo, 0, sizeof(snoopinfo));
-
- atexit(closedown);
-
- buildlist(__hMainWnd, ID_BUILDLIST);
-
- update_windows();
-
- return 0;
- }
-
-
-
-