/* Handle breakpoints - we expect to see breakpoints in the dynamic linker * (which we set ourselves) as well as profiler marks (embedded in the * profiled application's code). */ static bool ProcessBreakpoint( pid_t pid, u_long ip ) { static int ld_state = RT_CONSISTENT; // This ought to be per-pid int ptrace_sig = 0; #if defined( MD_x86 ) user_regs_struct regs; // on x86, when breakpoint was hit the EIP points to the next // instruction, so we must be careful ptrace( PTRACE_GETREGS, pid, NULL, ®s ); if( ip == Rdebug.r_brk + sizeof( opcode_type ) ) { #elif defined( MD_ppc ) if( ip == Rdebug.r_brk ) { #endif opcode_type brk_opcode = BRKPOINT; int status; int ret; /* The dynamic linker breakpoint was hit, meaning that * libraries are being loaded or unloaded. This gets a bit * tricky because we must restore the original code that was * at the breakpoint and execute it, but we still want to * keep the breakpoint. */ if( WriteMem( pid, &saved_opcode, Rdebug.r_brk, sizeof( saved_opcode ) ) != sizeof( saved_opcode ) ) printf( "WriteMem() #1 failed\n" ); ReadMem( pid, &Rdebug, (addr_off)DbgRdebug, sizeof( Rdebug ) ); dbg_printf( "ld breakpoint hit, state is " ); switch( Rdebug.r_state ) { case RT_ADD: dbg_printf( "RT_ADD\n" ); AddLibrary( pid, Rdebug.r_map ); ld_state = RT_ADD; break; case RT_DELETE: dbg_printf( "RT_DELETE\n" ); ld_state = RT_DELETE; break; case RT_CONSISTENT: dbg_printf( "RT_CONSISTENT\n" ); if( ld_state == RT_DELETE ) RemoveLibrary( pid, Rdebug.r_map ); ld_state = RT_CONSISTENT; break; default: dbg_printf( "error!\n" ); break; } #if defined( MD_x86 ) regs.eip--; ptrace( PTRACE_SETREGS, pid, NULL, ®s ); #endif // unset breakpoint, single step, re-set breakpoint if( ptrace( PTRACE_SINGLESTEP, pid, NULL, (void *)ptrace_sig ) < 0 ) perror( "ptrace()" ); do { // have to loop since SIGALRM can interrupt us here ret = waitpid( pid, &status, 0 ); } while( (ret < 0) && (errno == EINTR) ); if( ret == -1) perror( "waitpid()" ); if( WriteMem( pid, &brk_opcode, Rdebug.r_brk, sizeof( brk_opcode ) ) != sizeof( brk_opcode ) ) dbg_printf( "WriteMem() #2 failed with errno %d for pid %d, %d bytes (at %p)!\n", errno, pid, sizeof( brk_opcode ), Rdebug.r_brk ); return( true ); // indicate this was our breakpoint } else { dbg_printf( "Not an ld breakpoint, assuming mark\n" ); #if defined( MD_x86 ) return( ProcessMark( pid, ®s ) ); #endif } return( false ); } /* * Real time signal (SIGALRM) handler. All we really need to do is wake up * periodically to interrupt waitpid(), the signal handler need not do much * at all. */ static void alarm_handler( int signo ) { TimerTicked = true; } /* Install periodic real time alarm signal */ static void InstSigHandler( int msec_period ) { struct sigaction sigalrm; struct itimerval timer; sigalrm.sa_handler = (void *)alarm_handler; sigemptyset( &sigalrm.sa_mask ); sigalrm.sa_flags = 0; sigaction( SIGALRM, &sigalrm, NULL ); timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = msec_period * 1000; timer.it_value.tv_sec = 0; timer.it_value.tv_usec = msec_period * 1000; if( setitimer( ITIMER_REAL, &timer, NULL ) ) { InternalError( MsgArray[MSG_SAMPLE_6 - ERR_FIRST_MESSAGE] ); } } /* * Main sampler loop. We run the profiled application under the control of * ptrace(). The sampler installs a SIGALRM handler which ticks at the desired * rate. Whenever the SIGALRM interrupts our own waitpid(), we send a SIGSTOP * to the profiled app and when the child app notifies us of the SIGSTOP, we * remember the current execution point and continue the profiled app. Note * that we may miss some ticks but this is not a problem - the ticks don't * even need to be regular to provide usable results. */ static void SampleLoop( pid_t pid ) { static int ptrace_sig = 0; static bool do_cont = true; int status; user_regs_struct regs; bool sample_continue = true; int ret; opcode_type brk_opcode = BRKPOINT; TimerTicked = false; InstSigHandler( SleepTime ); do { if( do_cont && ptrace( PTRACE_CONT, pid, NULL, (void *)ptrace_sig ) == -1) perror( "ptrace()" ); ret = waitpid( pid, &status, 0 ); if( (ret < 0) && (errno == EINTR) ) { /* did we get woken up by SIGALRM? */ if( TimerTicked ) { TimerTicked = false; /* interrupt child process - next waitpid() will see this */ kill( pid, SIGSTOP ); } else { dbg_printf( "who the hell interrupted waitpid()?\n" ); } do_cont = false; continue; } if( ret < 0 ) perror( "waitpid()" ); do_cont = true; /* record current execution point */ #if defined( MD_x86 ) ptrace( PTRACE_GETREGS, pid, NULL, ®s ); #elif defined( MD_ppc ) regs.eip = ptrace( PTRACE_PEEKUSER, pid, REGSIZE * PT_NIP, NULL ); #endif if( WIFSTOPPED( status ) ) { /* If debuggee has dynamic section, try getting the r_debug struct * every time the child process stops. The r_debug data may not be * available immediately after the child process first loads. */ if( !HaveRdebug && (DbgDyn != NULL) ) { if( Get_ld_info( pid, DbgDyn, &Rdebug, &DbgRdebug ) ) { AddLibrary( pid, Rdebug.r_map ); HaveRdebug = true; /* Set a breakpoint in dynamic linker. That way we can be * informed on dynamic library load/unload events. */ ReadMem( pid, &saved_opcode, Rdebug.r_brk, sizeof( saved_opcode ) ); dbg_printf( "setting ld breakpoint at %p, old opcode was %X\n", Rdebug.r_brk, saved_opcode ); WriteMem( pid, &brk_opcode, Rdebug.r_brk, sizeof( brk_opcode ) ); } } sample_continue = false; switch( (ptrace_sig = WSTOPSIG( status )) ) { case SIGSEGV: dbg_printf( "SIGSEGV at %p\n", regs.eip ); sample_continue = true; break; case SIGILL: dbg_printf( "SIGILL at %p\n", regs.eip ); sample_continue = true; break; case SIGABRT: dbg_printf( "SIGABRT at %p\n", regs.eip ); sample_continue = true; break; case SIGINT: dbg_printf( "SIGINT at %p\n", regs.eip ); sample_continue = true; break; case SIGTRAP: dbg_printf( "SIGTRAP at %p\n", regs.eip ); if( ProcessBreakpoint( pid, regs.eip ) ) { // don't pass on SIGTRAP if we expected this breakpoint ptrace_sig = 0; } sample_continue = true; break; case SIGSTOP: /* presumably we were behind this SIGSTOP */ RecordSample( regs.eip, 1 ); ptrace_sig = 0; sample_continue = true; break; default: /* all other signals get passed down to the child and we let * the child handle them (or not handle and die) */ sample_continue = true; break; } } else if( WIFEXITED( status ) ) { dbg_printf( "WIFEXITED pid %d\n", pid ); report(); sample_continue = false; } else if( WIFSIGNALED( status ) ) { dbg_printf( "WIFSIGNALED pid %d\n", pid ); report(); sample_continue = false; } } while( sample_continue ); } static int GetExeNameFromPid( pid_t pid, char *buffer, int max_len ) { char procfile[24]; int len; sprintf( procfile, "/proc/%d/exe", pid ); len = readlink( procfile, buffer, max_len ); if( len < 0 ) len = 0; buffer[len] = '\0'; return( len ); }
/* * WindowProc - main window message handler */ LONG CALLBACK MainWindowProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { WORD cmd; about_info ai; HMENU mh; CommunicationBuffer *dbginfo; switch ( msg ) { case WM_CREATE: setupSystemMenu( hwnd ); MainLBox = CreateListBox( hwnd ); mh = GetMenu( hwnd ); if( ConfigData.auto_attatch ) { CheckMenuItem( mh, MENU_AUTO_ATTATCH, MF_BYCOMMAND | MF_CHECKED ); } if( ConfigData.continue_exception ) { CheckMenuItem( mh, MENU_EXCEPTION_CONTINUE, MF_BYCOMMAND | MF_CHECKED ); } LBPrintf( MainLBox, STR_DRNT_STARTED, AppName ); break; case WM_SIZE: MoveListBox( MainLBox, 0, 0, LOWORD( lparam ), HIWORD( lparam ) ); ClearAlert(); break; case WM_SYSCOMMAND: switch( wparam ) { case MENU_LOG_CURRENT_STATE: case MENU_LOG_OPTIONS: case MENU_TASK_CTL: SendMessage( hwnd, WM_COMMAND, wparam, 0 ); break; default: return( DefWindowProc( hwnd, msg, wparam, lparam ) ); } break; case WM_COMMAND: cmd = LOWORD( wparam ); switch( cmd ) { case LISTBOX_1: break; case MENU_EXCEPTION_CONTINUE: ConfigData.continue_exception = !ConfigData.continue_exception; mh = GetMenu( hwnd ); if( ConfigData.continue_exception ) { CheckMenuItem( mh, MENU_EXCEPTION_CONTINUE, MF_BYCOMMAND | MF_CHECKED ); } else { CheckMenuItem( mh, MENU_EXCEPTION_CONTINUE, MF_BYCOMMAND | MF_UNCHECKED ); } break; case MENU_LOG_VIEW: ViewLog(); break; case MENU_NEW_TASK: CallProcCtl( MENU_NEW_TASK, NULL, NULL ); break; case MENU_AUTO_ATTATCH: ConfigData.auto_attatch = ! ConfigData.auto_attatch; mh = GetMenu( hwnd ); if( ConfigData.auto_attatch ) { CheckMenuItem( mh, MENU_AUTO_ATTATCH, MF_BYCOMMAND | MF_CHECKED ); } else { CheckMenuItem( mh, MENU_AUTO_ATTATCH, MF_BYCOMMAND | MF_UNCHECKED ); } break; case MENU_SHOW_DIP_STATUS: ShowDIPStatus( hwnd ); break; case MENU_CLEAR: ClearListBox( MainLBox ); break; case MENU_SAVE_AS: SaveListBox( SLB_SAVE_AS, SaveExtra, "", AppName, hwnd, GetListBoxHwnd( MainLBox ) ); break; case MENU_SAVE: SaveListBox( SLB_SAVE_TMP, SaveExtra, ".\\drwat.txt", AppName, hwnd, GetListBoxHwnd( MainLBox ) ); break; case MENU_FONT: if( ChooseMonoFont( hwnd ) ) { SetListBoxFont( MainLBox ); } break; case MENU_MARK: ProcessMark( hwnd, Instance, MarkPrint ); break; case MENU_ABOUT: ai.owner = hwnd; ai.inst = Instance; ai.name = AllocRCString( STR_ABOUT_NAME ); ai.version = AllocRCString( STR_ABOUT_VERSION ); ai.first_cr_year = "1993"; ai.title = AllocRCString( STR_ABOUT_TITLE ); DoAbout( &ai ); FreeRCString( ai.name ); FreeRCString( ai.version ); FreeRCString( ai.title ); break; case MENU_HELP_CONTENTS: if( !WHtmlHelp( hwnd, DR_CHM_FILE, HELP_CONTENTS, 0 ) ) { WWinHelp( hwnd, DR_HELP_FILE, HELP_CONTENTS, 0 ); } break; case MENU_HELP_SRCH: if( !WHtmlHelp( hwnd, DR_CHM_FILE, HELP_PARTIALKEY, (HELP_DATA)"" ) ) { WWinHelp( hwnd, DR_HELP_FILE, HELP_PARTIALKEY, (HELP_DATA)"" ); } break; case MENU_HELP_ON_HELP: WWinHelp( hwnd, HELP_HELP_FILE, HELP_HELPONHELP, 0 ); break; case MENU_TASK_CTL: DisplayProcList(); break; case MENU_LOG_CURRENT_STATE: MakeLog( NULL ); break; case MENU_ERASE_LOG_FILE: EraseLog(); break; case MENU_LOG_OPTIONS: SetLogOptions( hwnd ); break; case MENU_EXIT: SendMessage( hwnd, WM_CLOSE, 0, 0L ); break; default: MessageBox( hwnd, "This function is not yet available", AppName, MB_OK ); break; } break; case DR_DEBUG_EVENT: dbginfo = (CommunicationBuffer *)lparam; dbginfo->action = DebugEventHandler( &dbginfo->dbginfo ); break; case WM_COPYDATA: { COPYDATASTRUCT *copydata; copydata = (COPYDATASTRUCT *)lparam; ProcessCommandLine( copydata->lpData ); } break; case WM_CLOSE: if( QueryEnd( hwnd ) ) { SendMessage( hwnd, WM_DESTROY, 0, 0L ); } break; case WM_DESTROY: FiniListBox( MainLBox ); PutProfileInfo(); DestroyMonoFonts(); WWinHelp( hwnd, DR_HELP_FILE, HELP_QUIT, 0 ); PostQuitMessage( 0 ); break; default: return( DefWindowProc( hwnd, msg, wparam, lparam ) ); } return( 0L ); } /* WindowProc */
/* * SpyWindowProc - handle messages for the spy appl. */ LRESULT CALLBACK SpyWindowProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { int check; HWND selwin; HWND hinthwnd; WORD cmdid = 0; RECT area; BOOL pausestate; BOOL spyallstate; about_info ai; HMENU mh; switch ( msg ) { case WM_CREATE: GetClientRect( hwnd, &area ); mh = GetMenu( hwnd ); area.top = area.bottom - statusHite; StatusHdl = HintWndCreate( hwnd, &area, Instance, NULL ); statusHite = SizeHintBar( StatusHdl ); SetHintText( StatusHdl, (MenuItemHint *)menuHints, sizeof( menuHints ) / sizeof( MenuItemHint ) ); if( SpyMainWndInfo.show_hints ) { CheckMenuItem( mh, SPY_SHOW_HELP, MF_CHECKED | MF_BYCOMMAND ); } else { hinthwnd = GetHintHwnd( StatusHdl ); ShowWindow( hinthwnd, SW_HIDE ); } CreateSpyBox( hwnd ); SET_WNDINFO( hwnd, (LONG_PTR)SpyListBox ); CreateSpyTool( hwnd ); ShowSpyTool( SpyMainWndInfo.show_toolbar ); if( SpyMainWndInfo.show_toolbar ) { CheckMenuItem( mh, SPY_SHOW_TOOLBAR, MF_CHECKED | MF_BYCOMMAND ); } LogInit( hwnd, Instance, SpyLogTitle ); CheckMenuItem( SpyMenu, SPY_AUTO_SCROLL, MF_CHECKED ); EnableMenuItem( SpyMenu, SPY_ADD_WINDOW, MF_GRAYED ); EnableMenuItem( SpyMenu, SPY_STOP, MF_GRAYED ); EnableMenuItem( SpyMenu, SPY_OFFON, MF_GRAYED ); if( SpyMainWndInfo.on_top ) { CheckMenuItem( mh, SPY_TOP, MF_CHECKED | MF_BYCOMMAND ); SetWindowPos( hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE ); } break; case WM_TIMER: // See comment on setUpForPick KillTimer( hwnd, wparam ); switch( wparam ) { case SPY_ADD_WINDOW: selwin = DoPickDialog( wparam ); if( selwin != NULL ) { setMultipleWindows( hwnd ); AddSelectedWindow( selwin ); } break; case SPY_PEEK_WINDOW: DoPickDialog( wparam ); break; case SPY_WINDOW: selwin = DoPickDialog( cmdid ); if( selwin != NULL ) { ClearSelectedWindows(); setSingleWindow( hwnd, selwin ); enableSpy(); AddSelectedWindow( selwin ); } break; } break; #ifdef __NT__ case WM_COPYDATA: HandleMessage( (LPMSG)((COPYDATASTRUCT *)lparam)->lpData ); break; #endif case WM_MENUSELECT: hinthwnd = GetHintHwnd( StatusHdl ); HintMenuSelect( StatusHdl, hwnd, wparam, lparam ); break; case WM_COMMAND: cmdid = LOWORD( wparam ); switch( cmdid ) { case SPY_SHOW_HELP: SpyMainWndInfo.show_hints = !SpyMainWndInfo.show_hints; mh = GetMenu( hwnd ); hinthwnd = GetHintHwnd( StatusHdl ); if( SpyMainWndInfo.show_hints ) { CheckMenuItem( mh, SPY_SHOW_HELP, MF_CHECKED | MF_BYCOMMAND ); showHintBar( hwnd ); } else { CheckMenuItem( mh, SPY_SHOW_HELP, MF_UNCHECKED | MF_BYCOMMAND ); ShowWindow( hinthwnd, SW_HIDE ); } GetClientRect( hwnd, &area ); ResizeSpyBox( area.right - area.left, area.bottom - area.top ); break; case SPY_SHOW_TOOLBAR: SpyMainWndInfo.show_toolbar = !SpyMainWndInfo.show_toolbar; mh = GetMenu( hwnd ); if( SpyMainWndInfo.show_toolbar ) { CheckMenuItem( mh, SPY_SHOW_TOOLBAR, MF_CHECKED | MF_BYCOMMAND ); } else { CheckMenuItem( mh, SPY_SHOW_TOOLBAR, MF_UNCHECKED | MF_BYCOMMAND ); } ShowSpyTool( SpyMainWndInfo.show_toolbar ); GetClientRect( hwnd, &area ); ResizeSpyBox( area.right - area.left, area.bottom - area.top ); break; case SPY_TOP: SpyMainWndInfo.on_top = !SpyMainWndInfo.on_top; mh = GetMenu( hwnd ); if( SpyMainWndInfo.on_top ) { CheckMenuItem( mh, SPY_TOP, MF_CHECKED | MF_BYCOMMAND ); SetWindowPos( hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE ); } else { CheckMenuItem( mh, SPY_TOP, MF_UNCHECKED | MF_BYCOMMAND ); SetWindowPos( hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE ); } break; case SPY_MARK: pausestate = SpyMessagesPaused; SpyMessagesPaused = FALSE; /* make sure marks are * always added */ ProcessMark( hwnd, Instance, markCallback ); SpyMessagesPaused = pausestate; break; case SPY_SET_FONT: if( ChooseMonoFont( hwnd ) ) { statusHite = SizeHintBar( StatusHdl ); ResetSpyListBox(); showHintBar( hwnd ); } break; case SPY_SAVE_AS: SaveListBox( SLB_SAVE_AS, SaveExtra, "", SpyName, hwnd, SpyListBox ); break; case SPY_SAVE: SaveListBox( SLB_SAVE_TMP, SaveExtra, ".\\wspy.txt", SpyName, hwnd, SpyListBox ); break; case SPY_LOG: if( LogToggle() ) { CheckMenuItem( SpyMenu, SPY_LOG, MF_BYCOMMAND | MF_CHECKED ); } else { CheckMenuItem( SpyMenu, SPY_LOG, MF_BYCOMMAND | MF_UNCHECKED ); CheckMenuItem( SpyMenu, SPY_PAUSE_LOG, MF_BYCOMMAND | MF_UNCHECKED ); } break; case SPY_CONFIG_LOG: LogConfigure(); break; case SPY_EXIT: ClearFilter(); DestroyWindow( hwnd ); break; case SPY_LIST_BOX: switch( GET_WM_COMMAND_CMD( wparam, lparam ) ) { case LBN_ERRSPACE: ClearSpyBox(); break; case LBN_DBLCLK: DoMessageSelDialog( hwnd ); break; } break; case SPY_SHOW_SELECTED_WINDOWS: spyallstate = spyAll; DoShowSelectedDialog( hwnd, &spyallstate ); if( spyallstate ) { doSpyAll( hwnd, spyallstate ); if( spyAll ) { SetSpyState( ON ); } break; } if( WindowCount == 0 ) { SetWindowText( hwnd, SpyName ); disableSpy(); break; } if( WindowCount == 1 ) { setSingleWindow( hwnd, WindowList[0] ); } else { setMultipleWindows( hwnd ); } if( SpyState == NEITHER ) { enableSpy(); } break; case SPY_HELP_CONTENTS: if( !WHtmlHelp( hwnd, "spy.chm", HELP_CONTENTS, 0 ) ) { WWinHelp( hwnd, "spy.hlp", HELP_CONTENTS, 0 ); } break; case SPY_HELP_SRCH: if( !WHtmlHelp( hwnd, "spy.chm", HELP_PARTIALKEY, (HELP_DATA)"" ) ) { WWinHelp( hwnd, "spy.hlp", HELP_PARTIALKEY, (HELP_DATA)"" ); } break; case SPY_HELP_ON_HELP: WWinHelp( hwnd, "winhelp.hlp", HELP_HELPONHELP, 0 ); break; case SPY_ABOUT: ai.owner = hwnd; ai.inst = Instance; ai.name = AllocRCString( STR_ABOUT_NAME ); ai.version = AllocRCString( STR_ABOUT_VERSION ); ai.first_cr_year = "1993"; ai.title = AllocRCString( STR_ABOUT_TITLE ); DoAbout( &ai ); FreeRCString( ai.name ); FreeRCString( ai.version ); FreeRCString( ai.title ); break; case SPY_AUTO_SCROLL: if( SpyMessagesAutoScroll ) { SpyMessagesAutoScroll = FALSE; CheckMenuItem( SpyMenu, SPY_AUTO_SCROLL, MF_UNCHECKED ); } else { SpyMessagesAutoScroll = TRUE; CheckMenuItem( SpyMenu, SPY_AUTO_SCROLL, MF_CHECKED ); } break; case SPY_PAUSE_LOG: if( SpyLogPauseToggle() ) { CheckMenuItem( SpyMenu, SPY_PAUSE_LOG, MF_BYCOMMAND | MF_CHECKED ); } else { CheckMenuItem( SpyMenu, SPY_PAUSE_LOG, MF_BYCOMMAND | MF_UNCHECKED ); } break; case SPY_PAUSE_MESSAGES: SpyMessagePauseToggle(); break; case SPY_CLEAR_MESSAGES: ClearSpyBox(); ClearMessageCount(); break; case SPY_MESSAGES_ASCFG: if( AutoSaveConfig ) { check = MF_UNCHECKED; AutoSaveConfig = FALSE; } else { AutoSaveConfig = TRUE; check = MF_CHECKED; } CheckMenuItem( SpyMenu, SPY_MESSAGES_ASCFG, check ); break; case SPY_MESSAGES_SAVE: DoSaveSpyConfig(); break; case SPY_MESSAGES_LOAD: DoLoadSpyConfig(); break; case SPY_MESSAGES_WATCH: case SPY_MESSAGES_STOP: DoMessageDialog( hwnd, cmdid ); break; case SPY_OFFON: if( SpyState != NEITHER ) { SetSpyState( !SpyState ); } break; case SPY_STOP: disableSpy(); ClearSelectedWindows(); SetWindowText( hwnd, SpyName ); break; case SPY_ANOTHER_WINDOW: if( SpyState == NEITHER || spyAll ) { SendMessage( hwnd, WM_COMMAND, GET_WM_COMMAND_MPS( SPY_WINDOW, 0, 0 ) ); } else { SendMessage( hwnd, WM_COMMAND, GET_WM_COMMAND_MPS( SPY_ADD_WINDOW, 0, 0 ) ); } break; case SPY_PEEK_WINDOW: case SPY_ADD_WINDOW: case SPY_WINDOW: setUpForPick( hwnd, cmdid ); break; case SPY_ALL_WINDOWS: doSpyAll( hwnd, !spyAll ); if( spyAll ) { SetSpyState( ON ); } break; } break; #ifdef __NT__ case WM_NOTIFY: if( ((NMHDR *)lparam)->code == NM_DBLCLK && ((NMHDR *)lparam)->idFrom == SPY_LIST_BOX ) { DoMessageSelDialog( hwnd ); } break; #endif case WM_CLOSE: PostMessage( hwnd, WM_COMMAND, GET_WM_COMMAND_MPS( SPY_EXIT, 0, 0 ) ); break; case WM_ENDSESSION: if( wparam ) { SpyFini(); } break; case WM_DESTROY: HintWndDestroy( StatusHdl ); HintFini(); StatusWndFini(); DestroyMonoFonts(); DestroySpyTool(); WWinHelp( hwnd, "spy.hlp", HELP_QUIT, 0 ); PostQuitMessage( 0 ); break; case WM_MOVE: GetWindowRect( hwnd, &area ); if( !SpyMainWndInfo.minimized ) { SpyMainWndInfo.last_xpos = SpyMainWndInfo.xpos; SpyMainWndInfo.last_ypos = SpyMainWndInfo.ypos; SpyMainWndInfo.xpos = area.left; SpyMainWndInfo.ypos = area.top; } break; case WM_SIZE: if( wparam != SIZE_MAXIMIZED && wparam != SIZE_MINIMIZED ) { GetWindowRect( hwnd, &area ); SpyMainWndInfo.xsize = area.right - area.left; SpyMainWndInfo.ysize = area.bottom - area.top; } else { SpyMainWndInfo.xpos = SpyMainWndInfo.last_xpos; SpyMainWndInfo.ypos = SpyMainWndInfo.last_ypos; } SpyMainWndInfo.minimized = ( wparam == SIZE_MINIMIZED ); GetClientRect( hwnd, &area ); area.top = area.bottom - statusHite; hinthwnd = GetHintHwnd( StatusHdl ); MoveWindow( hinthwnd, area.left, area.top, area.right - area.left, statusHite, TRUE ); ResizeSpyBox( LOWORD( lparam ), HIWORD( lparam ) ); ResizeSpyTool( LOWORD( lparam ), HIWORD( lparam ) ); showHintBar( hwnd ); return( DefWindowProc( hwnd, msg, wparam, lparam ) ); break; #if defined( __NT__ ) case WM_ERASEBKGND: { static RECT r; GetClientRect( hwnd, &r ); FillRect( (HDC)wparam, &r, (HBRUSH)(COLOR_BTNFACE + 1) ); return 1; } #endif default: return( DefWindowProc( hwnd, msg, wparam, lparam ) ); } return( 0 ); } /* SpyWindowProc */