/*++ Copyright (c) 1992 Microsoft Corporation Module Name: cmdwin.c Abstract: This file contains the window procedure code for dealing with the command window. This window uses the document manager to deal with keeping track of the characters in the buffer. This window has the following strange properties: It is read-only except on the last line The first portion of the last line is read-only Author: Jim Schaad (jimsch) Environment: Win32 - User --*/ /************ INCLUDE FILES ******/ #include "precomp.h" #pragma hdrstop #include extern LPSHF Lpshf; extern CXF CxfIp; /************ Externals ***********/ BOOL FCmdDoingInput; extern BOOL fWaitForDebugString; extern LPSTR lpCmdString; extern ADDR addrLastDisasmStart; /************ Internal Prototypes ***********/ /************ Data declaration ***********/ static BOOL FAutoRunSuppress = FALSE; PCTRLC_HANDLER pPolledCtrlCHandler = NULL; BOOL fCtrlCPressed = FALSE; // Ctrl-C pressed flag for EditWndProc static PCTRLC_HANDLER PcchHead = NULL; static BOOL fEatEOLWhitespace = TRUE; // eat space at end of line static BOOL FAutoHistOK = FALSE; static char szOldPrompt[PROMPT_SIZE]; /************ Code ***********/ void NEAR PASCAL SelPosXY (int view, int X, int Y); void BPCallbackHbpt( HBPT hBpt, BPSTATUS bpstatus ) /*++ Routine Description: This function will be called for each breakpoint which mets its criteria Arguments: hBpt - Supplies breakpoint which met the breakpoint criteria bpstatus - Supplies breakpoint status code during evaluation Return Value: None --*/ { ULONG i; char rgchT[256]; if (bpstatus != BPNOERROR) { Dbg( BPIFromHbpt( &i, hBpt ) == BPNOERROR); CmdInsertInit(); CmdLogFmt("Error checking breakpoint #%d\r\n", i); return; } if (LptdCur->fInFuncEval == FALSE) { Dbg( BPIFromHbpt( &i, hBpt) == BPNOERROR ); CmdInsertInit(); CmdLogFmt("Breakpoint #%d hit\r\n", i); Dbg( BPQueryCmdOfHbpt( hBpt, rgchT, sizeof(rgchT)-1) == BPNOERROR ); if (*rgchT != 0) { CmdPrependCommands(LptdCur, rgchT); } } return; } /* BPCallbackHbpt() */ BOOL DoStopEvent( LPTD lptd ) /*++ Routine Description: This routine is called whenever a thread stops. It handles any special processing that should be done when a thread stops, such as print the registers or executing special commands associated with stopping. Arguments: lptd - Supplies the thread that stopped Return Value: TRUE if the caller should be silent. FALSE if normal processing should continue, and normal messages should be printed. --*/ { if (*szStopEventCmd) { CmdPrependCommands(lptd, szStopEventCmd); } if (lptd && lptd->fDisasm && !AutoTest) { CmdPrependCommands(lptd, "u . l1"); lptd->fDisasm = FALSE; } if (lptd && lptd->fRegisters) { CmdPrependCommands(lptd, "r"); lptd->fRegisters = FALSE; } // forget where we disassembled last, so that "u" will default to // the current IP. addrLastDisasmStart.addr.seg = 0; addrLastDisasmStart.addr.off = 0; return FALSE; } BOOL CmdHandleInputString( LPSTR lpsz ) { CmdSetDefaultPrompt( szOldPrompt ); CmdSetDefaultCmdProc(); OSDInfoReply(LppdCur->hpid, LptdCur->htid, lpsz, strlen(lpsz) + 1); return TRUE; } VOID GetPointersFromQueue( HPID * phpid, HTID * phtid, LPPD * plppd, LPTD * plptd ) { *plptd = (LPTD) GetQueueItemQuad(); if (*plptd != 0) { *plppd = (*plptd)->lppd; *phtid = (*plptd)->htid; *phpid = (*plppd)->hpid; } else { *phpid = (HPID)GetQueueItemQuad(); *phtid = (HTID)GetQueueItemQuad(); *plppd = LppdOfHpid(*phpid); Assert(*plppd); *plptd = LptdOfLppdHtid(*plppd, *phtid); } } BOOL CmdExecNext( DBC unusedIW, LPARAM unusedL ) /*++ Routine Description: This function causes two things to occur. First the command window is updated to reflect the command/event which just occurred and secondly the command processor state machine is consulted to see if there are any more commands to be executed from the command window. Arguments: dbcCallback - Supplies the call back which caused this update to occur lParam - Supplies information about the callback Return Value: TRUE if update the screen and FALSE otherwise --*/ { long lExit; LPSTR lpch; LPSTR lpsz; long l; DWORD dw; int dbcCallback; HEXE emi; SHE she; BPSTATUS bpstatus; XOSD xosd; CXT cxt; ADDR addr; DWORD64 qwNotify; EXOP exop; HPDS HpdsOld; HPID hpid; HTID htid; LPPD lppd; LPTD lptd; LPPD LppdT; LPTD LptdT; BOOL fExecNext; int bfRefresh; BOOL fGetFocus; BOOL fRunning; BOOL fReply; int iter = 0; BOOL ThreadIsStopped; static int fRecurse = FALSE; extern BOOL FKilling; if (fRecurse) { return TRUE; } fRecurse = TRUE; // This call will AV if __try { while (GetQueueLength() != 0) { if (iter++ == 10) { fRecurse = FALSE; PostMessage(hwndFrame, DBG_REFRESH, 0, 0); return FALSE; } /* * Inititalization of variables; */ LppdT = LppdCur; LptdT = LptdCur; fExecNext = TRUE; bfRefresh = UPDATE_ALLDBGWIN; fGetFocus = FALSE; /* * Get the next command to be processed */ dbcCallback = (int) GetQueueItemQuad(); #if 0 // BUGBUG -- jls Need to put in new code to deal with this problem. if ((DbgState == ds_error) && (dbcCallback != dbcInfoAvail)) { fRecurse = FALSE; return FALSE; } #endif /* * Set up the command window to allow for doing input. */ CmdInsertInit(); /* */ switch ((DBC)dbcCallback) { case dbcError: /* DBG_INFO */ bfRefresh = UPDATE_NONE; hpid = (HPID)GetQueueItemQuad(); htid = (HTID)GetQueueItemQuad(); xosd = (XOSD)GetQueueItemQuad(); lpsz = (LPSTR)GetQueueItemQuad(); LppdCur = LppdOfHpid(hpid); LptdCur = LptdOfLppdHtid(LppdCur, htid); CmdLogFmt("ERROR - %s\r\n", lpsz); MHFree(lpsz); fGetFocus = TRUE; fExecNext = FALSE; if (LppdCur->pstate == psPreRunning) { /* * we aren't going to get the breakpoint... */ LppdCur->pstate = psRunning; for (lptd = LppdCur->lptdList; lptd; lptd = lptd->lptdNext) { lptd->tstate = tsRunning; } Dbg( BPFreeHbpt( (HBPT)LppdCur->hbptSaved ) == BPNOERROR) ; LppdCur->hbptSaved = NULL; BPTResolveAll(LppdCur->hpid,TRUE); SetProcessExceptions(LppdCur); VarMsgBox(NULL, DBG_Attach_Deadlock, MB_OK | MB_ICONINFORMATION|MB_SETFOREGROUND|MB_TASKMODAL); } // Any time we update the context we must invalidate the Watch window. bfRefresh = UPDATE_CONTEXT | UPDATE_ALLDBGWIN | UPDATE_SYMBOLS_CHANGED; break; case dbcLoadComplete: lptd = (LPTD) GetQueueItemQuad(); Assert(lptd != NULL); qwNotify = GetQueueItemQuad(); LptdCur = lptd; LppdCur = lptd->lppd; Assert(LppdCur->pstate == psPreRunning); LptdCur->tstate = tsStopped; // Any time we update the context we must invalidate the Watch window. UpdateDebuggerState(UPDATE_CONTEXT | UPDATE_SYMBOLS_CHANGED | UPDATE_WATCH); BPTResolveAll(LppdCur->hpid,TRUE); /* * The process is no longer "PreRunning", * but the thread is. */ LppdCur->pstate = psStopped; SetProcessExceptions(LppdCur); #ifdef SHOW_MAGIC CmdLogFmt("(Process loaded)\r\n"); #endif fExecNext = FALSE; bfRefresh = UPDATE_NONE; /* * Handle go/stop for child here. * For attachee, it is handled in AttachDebuggee() */ if (LppdCur->fChild) { if (g_contWorkspace_WkSp.m_bChildGo) { Go(); } else { DoStopEvent(lptd); lptd->lppd->fStopAtEntry = FALSE; fGetFocus = TRUE; fExecNext = TRUE; bfRefresh = UPDATE_ALLDBGWIN; } } break; case dbcEntryPoint: lptd = (LPTD) GetQueueItemQuad(); Assert(lptd != NULL); qwNotify = GetQueueItemQuad(); LptdCur = lptd; lppd = LppdCur = lptd->lppd; LptdCur->tstate = tsStopped; UpdateDebuggerState(UPDATE_CONTEXT); fRunning = FALSE; if (lppd->fInitialStep) { // If stepping in source mode, // we will land here. // If we are in "regular" mode, try // to find main or WINMAIN or whatever. if (!g_contWorkspace_WkSp.m_bEPIsFirstStep) { cxt = *SHpCXTFrompCXF(&CxfIp); fRunning = get_initial_context(&cxt, FALSE); //fRunning = get_initial_context(&cxt, TRUE); if (fRunning) { SYFixupAddr(SHpAddrFrompCxt(&cxt)); //v-vadimp - skip the prolog on ia64 // kentf - I think this is the wrong place to do this, // and you probably shouldn't get the focus // until later. if(LppdCur->mptProcessorType == mptia64) { while( SHIsInProlog( &cxt ) ) { cxt.addr.addr.off++; } } GoUntil( SHpAddrFrompCxt(&cxt) ); } else { VarMsgBox (NULL, ERR_NoSymbols, MB_OK|MB_ICONINFORMATION|MB_SETFOREGROUND|MB_TASKMODAL); } fGetFocus = TRUE; } } else if (lppd->hbptSaved) { if (!get_initial_context(SHpCXTFrompCXF(&CxfIp), FALSE)) { get_initial_context(SHpCXTFrompCXF(&CxfIp), TRUE); } if (BPBindHbpt( (HBPT)lppd->hbptSaved, &CxfIp ) != BPNOERROR) { Dbg( BPFreeHbpt( (HBPT)lppd->hbptSaved ) == BPNOERROR) ; lppd->hbptSaved = NULL; CmdLogVar(ERR_Unable_To_Complete_Gountil); } else { Dbg( BPAddrFromHbpt( (HBPT)lppd->hbptSaved, &addr ) == BPNOERROR) ; Dbg( BPFreeHbpt( (HBPT)lppd->hbptSaved ) == BPNOERROR) ; lppd->hbptSaved = NULL; fRunning = TRUE; GoUntil(&addr); } } else if (!lppd->fStopAtEntry) { // if stepping in ASM mode, fStopAtEntry // is like a safety BP to stop running after // leaving the loader APC. fRunning = TRUE; Go(); } if (fRunning) { fExecNext = FALSE; bfRefresh = UPDATE_NONE; LppdCur = LppdT; LptdCur = LptdT; } else { BPClearAllTmp( lppd->hpid, lptd->htid ); if (!DoStopEvent(lptd)) { CmdLogVar(DBG_At_Entry_Point); } } lppd->fInitialStep = FALSE; lppd->fStopAtEntry = FALSE; break; case dbcCheckBpt: Assert(!"dbcCheckBpt should not get here."); break; case dbcBpt: /* DBG_REFRESH */ lptd = (LPTD) GetQueueItemQuad(); Assert(lptd != NULL); qwNotify = GetQueueItemQuad(); LptdCur = lptd; LppdCur = lptd->lppd; LptdCur->tstate = tsStopped; UpdateDebuggerState(UPDATE_CONTEXT); if ( FKilling || !DebuggeeActive() ) { LppdCur = LppdT; LptdCur = LptdT; } else { bpstatus = BPCheckHbpt( CxfIp, BPCallbackHbpt, lptd->lppd->hpid, lptd->htid, qwNotify ); if (bpstatus == BPNoBreakpoint) { BPClearAllTmp( lptd->lppd->hpid, lptd->htid ); DoStopEvent(lptd); lptd->lppd->fStopAtEntry = FALSE; fGetFocus = TRUE; CmdLogVar(DBG_Hard_Coded_Breakpoint); } else if (bpstatus == BPPassBreakpoint) { /* * -- do a go as no breakpoint was matched */ //Assert(!"Unexpected BPPassBreakpoint"); fExecNext = FALSE; bfRefresh = UPDATE_NONE; Go(); LppdCur = LppdT; LptdCur = LptdT; } else { BPClearAllTmp( lptd->lppd->hpid, lptd->htid ); DoStopEvent(lptd); lptd->lppd->fStopAtEntry = FALSE; fGetFocus = TRUE; } } break; case dbcAsyncStop: /* DBG_REFRESH */ fExecNext = FALSE; case dbcStep: bfRefresh = UPDATE_ALLDBGWIN | UPDATE_CONTEXT; lptd = (LPTD) GetQueueItemQuad(); Assert(lptd != NULL); qwNotify = GetQueueItemQuad(); LptdCur = lptd; LppdCur = lptd->lppd; SetPTState(psInvalidState, tsStopped); BPClearAllTmp( lptd->lppd->hpid, lptd->htid ); if (!DoStopEvent(lptd)) { if (dbcCallback == dbcAsyncStop) { CmdLogFmt("Stopped in process %d, thread %d\r\n", lptd->lppd->ipid, lptd->itid); } } if (dbcCallback == dbcAsyncStop) { LptdCur->cStepsLeft = 0; } lptd->lppd->fStopAtEntry = FALSE; fGetFocus = TRUE; break; case dbcModLoad: /* DBG_INFO */ lptd = (LPTD)GetQueueItemQuad(); if (lptd == NULL) { // this is the first modload, which is really // part of the process creation. The thread isn't // here yet. hpid = (HPID)GetQueueItemQuad(); htid = (HTID)GetQueueItemQuad(); lppd = LppdOfHpid(hpid); lptd = LptdOfLppdHtid(lppd, htid); } else { lppd = lptd->lppd; hpid = lppd->hpid; htid = lptd->htid; } lpsz = (LPSTR)GetQueueItemQuad(); ThreadIsStopped = (BOOL)GetQueueItemQuad(); Assert(lppd); Assert(lpsz); // Any time we update the context we must invalidate the // Watch window. bfRefresh = UPDATE_ALLDBGWIN | UPDATE_SYMBOLS_CHANGED; // set the SH to this process, load the symbols, // tell the EM, and reset the SH. HpdsOld = SHChangeProcess(lppd->hpds); SetFindExeBaseName(lppd->lpBaseExeName); she = SHLoadDll( lpsz, TRUE ); emi = SHGethExeFromName((char FAR *) lpsz); Assert(emi != 0); OSDRegisterEmi( hpid, (HEMI)emi, lpsz); MHFree(lpsz); SHChangeProcess(HpdsOld); // update the modload status report ModListModLoad( SHGetExeName( emi ), she ); // If this is the first modload, remember it as the base exe if (!lppd->lpBaseExeName) { lppd->lpBaseExeName = MHStrdup(SHGetExeName( emi )); } // spew to the cmdwin if (she != sheSuppressSyms && g_contWorkspace_WkSp.m_bVerbose) { lpch = SHLszGetErrorText(she); if (she == sheNoSymbols) { CmdLogFmt("Module Load: %s", SHGetExeName( emi )); } else { CmdLogFmt("Module Load: %s", SHGetSymFName( emi )); } if (lpch) { CmdLogFmt(" (%s)", lpch); } CmdLogFmt("\r\n"); } if (lppd->pstate != psPreRunning) { // A new module has been loaded. If we are not loading // the statically-linked DLLs of the debuggee, we must // try to resolve any unresolved breakpoints. if (BPTIsUnresolvedCount( hpid )) { LptdCur = lptd; LppdCur = lppd; UpdateDebuggerState( bfRefresh ); BPTResolveAll( hpid, FALSE ); LppdCur = LppdT; LptdCur = LptdT; bfRefresh |= UPDATE_CONTEXT; } } // Continue the thread or stay stopped: if (!ThreadIsStopped) { fExecNext = FALSE; } else if (g_contWorkspace_WkSp.m_bGoOnModLoad || lptd == NULL) { fExecNext = FALSE; OSDGo(hpid, htid, &exop); } else { LptdCur = lptd; LppdCur = lppd; SetPTState(psInvalidState, tsStopped); fExecNext = TRUE; fGetFocus = TRUE; } // When debugging memory dumps, the loading of the kernel // needs to be synchronous, else data structures in the DM // will not be initialized to sane values. if ( g_contWorkspace_WkSp.m_bKernelDebugger && g_contKernelDbgPreferences_WkSp.m_bUseCrashDump) { TCHAR szFName[_MAX_FNAME]; TCHAR szExt[_MAX_EXT]; _tsplitpath(lppd->lpBaseExeName, 0, 0, szFName, szExt); if (!_tcsicmp(szExt, _T(".EXE")) && ( !_tcsicmp(szFName, _T("NTOSKRNL")) || !_tcsicmp(szFName, _T("NTKRNLMP")) ) ) { OSDSignalKernelLoadCompleted(hpid); } } break; case dbcModFree: /* DBG_REFRESH */ // Any time we update the context we must invalidate the // Watch window. bfRefresh = UPDATE_CONTEXT | UPDATE_SYMBOLS_CHANGED | UPDATE_WATCH; lptd = (LPTD)GetQueueItemQuad(); if (lptd == NULL) { hpid = (HPID)GetQueueItemQuad(); htid = (HTID)GetQueueItemQuad(); lppd = LppdOfHpid(hpid); lptd = LptdOfLppdHtid(lppd, htid); } else { lppd = lptd->lppd; hpid = lppd->hpid; htid = lptd->htid; } Assert(lppd); Assert(lptd); emi = (HEXE) GetQueueItemQuad(); Assert(emi != 0); HpdsOld = SHChangeProcess(lppd->hpds); lpsz = SHGetExeName( emi ); // unloading user mode modules in kernel mode // can result in nameless emis if (lpsz && *lpsz) { ModListModUnload( SHGetExeName( emi ) ); } if (g_contWorkspace_WkSp.m_bVerbose && !ExitingDebugger) { if (SHIsDllLoaded(emi) && lpsz) { CmdLogFmt("Module Unload: %s\r\n", lpsz); } } // A module has been unloaded. We must unresolve all // the breakpoints in the module. if (lppd->pstate == psRunning) { BPTUnResolve( emi ); } OSDUnRegisterEmi(hpid, (HEMI) emi); SHUnloadDll( emi ); fExecNext = FALSE; break; case dbcCreateThread: /* DBG_INFO */ bfRefresh = UPDATE_NONE; fExecNext = FALSE; Dbg(GetQueueItemQuad() == 0); hpid = (HPID)GetQueueItemQuad(); htid = (HTID)GetQueueItemQuad(); lppd = LppdOfHpid(hpid); if (!lppd && !LppdFirst) { // when blowing off the debuggee, some stray events // may be in this queue... break; } Assert(lppd); lptd = CreateTd(lppd, htid); Assert(lptd); LppdT = LppdCur = lppd; LptdT = LptdCur = lptd; if (g_contWorkspace_WkSp.m_bNotifyThreadCreate) { CmdLogFmt("Thread Create: Process=%d, Thread=%d\r\n", lptd->lppd->ipid, lptd->itid); } // Don't do the update and BPTResolveAll if this is a // thread which is being created when we are already // stopped at a debug event. if (!IsProcStopped(lppd)) { UpdateDebuggerState( UPDATE_CONTEXT ); if (LppdCur->pstate == psRunning) { BPTResolveAll( hpid, TRUE ); } SetPTState(psInvalidState, tsStopped); Go(); } break; case dbcThreadTerm: /* DBG_REFRESH */ lptd = (LPTD) GetQueueItemQuad(); if ( lptd == 0) { hpid = (HPID)GetQueueItemQuad(); htid = (HTID)GetQueueItemQuad(); lppd = LppdOfHpid(hpid); lptd = LptdOfLppdHtid(lppd, htid); } lExit = (long) GetQueueItemQuad(); if (!lptd && !LppdFirst) { // when blowing off the debuggee, some stray events // may be in this queue... break; } Assert(lptd != NULL); lppd = lptd->lppd; LppdCur = lppd; LptdCur = lptd; SetPTState(psInvalidState, tsExited); if (g_contWorkspace_WkSp.m_bNotifyThreadTerm) { CmdLogFmt("Thread Terminate: Process=%d, Thread=%d, Exit Code=%ld\r\n", lptd->lppd->ipid, lptd->itid, lExit); } if (lptd->fInFuncEval) { fExecNext = FALSE; LptdFuncEval = NULL; } BPTUnResolvePidTid( lppd->hpid, lptd->htid ); if (lptd->fGoOnTerm || g_contWorkspace_WkSp.m_bGoOnThreadTerm || !g_contWorkspace_WkSp.m_bNotifyThreadTerm) { Go(); fExecNext = FALSE; bfRefresh = UPDATE_NONE; } else { DoStopEvent(NULL); lptd->lppd->fStopAtEntry = FALSE; fGetFocus = TRUE; bfRefresh |= UPDATE_NOFORCE; } break; case dbcDeleteThread: /* DBG_INFO */ // Any time we update the context we must invalidate the Watch window. bfRefresh = UPDATE_WINDOWS | UPDATE_SYMBOLS_CHANGED; lptd = (LPTD) GetQueueItemQuad(); if ( lptd ) { lppd = lptd->lppd; Assert(lppd != NULL); #ifdef SHOW_MAGIC CmdLogFmt("DBG: Thread Destroy: Process=%d, Thread=%d\r\n", lptd->lppd->ipid, lptd->itid); #endif if (lptd->fInFuncEval) { LptdFuncEval = NULL; } OSDDestroyHtid(lptd->lppd->hpid, lptd->htid); DestroyTd(lptd); if (LppdCur == lppd && LptdCur == lptd) { LptdCur = NULL; } lptd = NULL; } fExecNext = FALSE; break; case dbcNewProc: /* DBG_INFO */ bfRefresh = UPDATE_NONE; /* * This will occur on all but the first process created. */ hpid = (HPID)GetQueueItemQuad(); lppd = LppdOfHpid(hpid); Assert(lppd == NULL); lppd = CreatePd(hpid); SetPdInfo(lppd); lppd->hpds = SHCreateProcess(); SHSetHpid(hpid); SetProcessExceptions(lppd); /* * proc is PreRunning until ldr BP */ lppd->pstate = psPreRunning; /* * If it is an attach, not a child, AttachDebuggee() will * clear this flag in a moment. */ lppd->fChild = TRUE; CmdLogFmt("Process Create: Process=%d\r\n", lppd->ipid); /* * This won't be the current process yet, because * there isn't a thread for it until we get notified. */ fExecNext = FALSE; break; case dbcProcTerm: /* DBG_INFO */ bfRefresh = UPDATE_NONE; lppd = LppdOfHpid((HPID) GetQueueItemQuad()); LppdCur = lppd; LptdCur = NULL; SetPTState(psExited, tsInvalidState); lExit = (long) GetQueueItemQuad(); if (!FKilling) { CmdLogFmt("Process Terminate: Process=%d, Exit Code=%ld\r\n", lppd->ipid, lExit); } /* * Unresolve all the breakpoints. This way they can be * resolved again upon restarting. */ BPClearAllTmp( lppd->hpid, 0 ); BPTUnResolveAll(lppd->hpid); fExecNext = FALSE; LppdCur = NULL; LptdCur = NULL; bfRefresh |= UPDATE_NOFORCE; break; case dbcDeleteProc: /* DBG_INFO */ bfRefresh = UPDATE_NONE; hpid = (HPID)GetQueueItemQuad(); lppd = LppdOfHpid(hpid); #ifdef SHOW_MAGIC CmdLogFmt("DBG: Process Destroy: Process=%d\r\n", lppd->ipid); #endif SHChangeProcess(lppd->hpds); while ( emi = SHGetNextExe( (HEXE) NULL ) ) { SHUnloadDll( emi ); } ClearProcessExceptions(lppd); if (!lppd->fPrecious) { SHDeleteProcess(lppd->hpds); OSDDestroyHpid(hpid); } DestroyPd(lppd, FALSE); RecycleIpid1(); if (lppd == LppdCur) { LppdCur = NULL; LptdCur = NULL; } else if (LppdCur) { SHChangeProcess(LppdCur->hpds); // Any time we update the context we must invalidate the Watch window. bfRefresh |= (UPDATE_NOFORCE | UPDATE_CONTEXT | UPDATE_WINDOWS | UPDATE_SYMBOLS_CHANGED); } break; case dbcInfoAvail: /* DBG_INFO */ bfRefresh = UPDATE_NONE; fExecNext = FALSE; fReply = (BOOL) GetQueueItemQuad(); // Reply flag if (!GetQueueItemQuad()) { /* fUniCode */ // ANSI lpsz = (LPSTR) GetQueueItemQuad(); /* String */ CmdLogDebugString( lpsz, TRUE ); } else { // Unicode LPWSTR lpw; lpw = (LPWSTR) GetQueueItemQuad(); /* String */ l = WideCharToMultiByte(CP_ACP, 0, lpw, -1, NULL, 0, NULL, NULL ); lpsz = (PSTR) MHAlloc(l+1); Assert(lpsz); WideCharToMultiByte(CP_ACP, 0, lpw, -1, lpsz, l+1, NULL, NULL ); CmdLogDebugString(lpsz, TRUE); MHFree(lpw); } if (fWaitForDebugString) { char *lpsz1=lpsz; while (lpsz1 && *lpsz1) { if (*lpsz1 == '\r' || *lpsz1 == '\n') { *lpsz1 = '\0'; } else { lpsz1 = CharNext(lpsz1); } } if (_stricmp(lpsz, lpCmdString)==0) { MHFree(lpCmdString); fExecNext = TRUE; } } MHFree(lpsz); if (fReply && LppdCur) { OSDInfoReply(LppdCur->hpid, LptdCur->htid, NULL, 0); } break; case dbcInfoReq: bfRefresh = UPDATE_NONE; fExecNext = TRUE; if (!GetQueueItemQuad()) { /* fUniCode */ // ANSI lpsz = (LPSTR) GetQueueItemQuad(); /* String */ } else { // Unicode LPWSTR lpw; lpw = (LPWSTR) GetQueueItemQuad(); /* String */ l = WideCharToMultiByte(CP_ACP, 0, lpw, -1, NULL, 0, NULL, NULL ); lpsz = (PSTR) MHAlloc(l+1); Assert(lpsz); WideCharToMultiByte(CP_ACP, 0, lpw, -1, lpsz, l+1, NULL, NULL ); MHFree(lpw); } CmdGetDefaultPrompt(szOldPrompt); CmdSetDefaultPrompt(lpsz); CmdSetCmdProc(CmdHandleInputString, CmdExecutePrompt); CmdDoPrompt(TRUE, TRUE); FCmdDoingInput = TRUE; MHFree(lpsz); break; case dbcException: /* DBG_REFRESH */ { EXCEPTION_LIST *eList; LPEPR lpepr; lptd = (LPTD) GetQueueItemQuad(); if (lptd != 0) { lppd = lptd->lppd; htid = lptd->htid; hpid = lppd->hpid; } else { hpid = (HPID)GetQueueItemQuad(); htid = (HTID)GetQueueItemQuad(); lppd = LppdOfHpid(hpid); Assert(lppd); lptd = LptdOfLppdHtid(lppd, htid); } LppdCur = lppd; LptdCur = lptd; lpepr = (LPEPR) GetQueueItemQuad(); Assert(lpepr); if (LppdCur->pstate == psPreRunning) { /* * We hit an exception before the process * had finished loading. We probably won't ever * hit the loader BP, so mark this as loaded now. */ LppdCur->pstate = psStopped; BPTResolveAll(LppdCur->hpid,TRUE); SetProcessExceptions(LppdCur); #ifdef SHOW_MAGIC CmdLogFmt("(Exception caught while loading)\r\n"); #endif } for ( eList = LppdCur->exceptionList; eList; eList = eList->next ) { if ( eList->dwExceptionCode == lpepr->ExceptionCode ) { break; } } // The local list should always match the DM's list // except on second chance, when the DM always punts Assert(eList == 0 || lpepr->efd == eList->efd || (!lpepr->dwFirstChance && (lpepr->efd == efdStop || lpepr->efd == efdCommand))); if (lpepr->dwFirstChance) { SetPTState(psInvalidState, tsException1); } else { SetPTState(psInvalidState, tsException2); } AuxPrintf(1, "Exception %d, FirstChance == %d, efd == %d", lpepr->ExceptionCode, lpepr->dwFirstChance, lpepr->efd); switch (lpepr->efd) { case efdNotify: SetPTState(psInvalidState, tsRunning); case efdStop: case efdCommand: CmdLogVar((WORD)((lpepr->dwFirstChance)? DBG_Exception1_Occurred : DBG_Exception2_Occurred), lpepr->ExceptionCode, (eList == NULL || eList->lpName == NULL) ? "Unknown" : eList->lpName); if (lpepr->efd == efdNotify) { bfRefresh = UPDATE_NONE; } else { fGetFocus = TRUE; lptd->lppd->fStopAtEntry = FALSE; if ( eList ) { if ( lpepr->dwFirstChance && eList->lpCmd ) { CmdPrependCommands(lptd, eList->lpCmd); } else if ( !lpepr->dwFirstChance && eList->lpCmd2 ) { CmdPrependCommands(lptd, eList->lpCmd2); } } if (!DoStopEvent(lptd)) { CmdLogVar(DBG_Thread_Stopped); } } break; case efdIgnore: bfRefresh = UPDATE_NONE; CmdLogFmt("WINDBG: benign error - \r\n efdIgnore should never reach the shell - \r\n"); CmdLogFmt(" ExceptionCode == %08x\r\n", lpepr->ExceptionCode); break; default: // undefined efd value bfRefresh = UPDATE_NONE; CmdLogFmt("INTERNAL ERROR: unrecognized efd %d\r\n", lpepr->efd); Assert(FALSE); break; } MHFree(lpepr); break; } /* * This message is recieved when a function call from the * expression evaluator is finished. * DO NOTHING */ case dbcExecuteDone: /* DBG_INFO */ bfRefresh = UPDATE_NONE; fRecurse = FALSE; fExecNext = FALSE; break; case dbcServiceDone: // Some Ioctl has finished executing // Signal the synchronization event to release the command window GetQueueItemQuad(); SetEvent( hEventIoctl ); bfRefresh = UPDATE_NONE; fExecNext = FALSE; break; case dbcCanStep: CmdLogFmt("WINDBG: dbcCanStep is obsolete\r\n"); RAssert(FALSE); break; case (DBC)dbcRemoteQuit: bfRefresh = UPDATE_NONE; CmdLogFmt("Connection to remote has been broken\r\n" ); CmdLogFmt("Stopped debugging\r\n" ); DisconnectDebuggee(); fRecurse = FALSE; return bfRefresh; case (DBC)dbcMemoryChanged: bfRefresh = UPDATE_NONE; BPUpdateMemory( (ULONG) GetQueueItemQuad() ); fExecNext = FALSE; bfRefresh = UPDATE_NONE; break; case dbcSegLoad: lptd = (LPTD)GetQueueItemQuad(); Assert(lptd != NULL); lppd = lptd->lppd; LppdCur = lppd; LptdCur = lptd; bfRefresh = UPDATE_NONE; fExecNext = FALSE; SHChangeProcess(lppd->hpds); BPSegLoad( (ULONG)GetQueueItemQuad() ); Go(); break; case dbcExitedFunction: lptd = (LPTD) GetQueueItemQuad(); if (lptd == NULL) { hpid = (HPID)GetQueueItemQuad(); htid = (HTID)GetQueueItemQuad(); } // return value: dw = (DWORD) GetQueueItemQuad(); fExecNext = FALSE; bfRefresh = UPDATE_NONE; break; case (DBC)dbcCommError: default: lptd = (LPTD) GetQueueItemQuad(); if (lptd == NULL) { hpid = (HPID)GetQueueItemQuad(); htid = (HTID)GetQueueItemQuad(); } CmdLogFmt("WINDBG: Unknown DBC %x\r\n", dbcCallback); fExecNext = FALSE; break; } // Thread and/or process may have been changed - try to make // sure that both are valid, and update status line. if (LptdCur != NULL) { LppdCur = LptdCur->lppd; } else if (LppdCur != NULL) { LptdCur = LppdCur->lptdList; } if (LptdCur == NULL) { GetFirstValidPDTD(&LppdCur, &LptdCur); } if (LppdCur == NULL) { AuxPrintf(1, "(CmdExecNext: There are no processes)"); } else if (LptdCur == NULL) { AuxPrintf(1, "(CmdExecNext: There are no threads)"); } else if (LppdCur != LppdT || LptdCur != LptdT) { // Any time we update the context we must invalidate the // Watch window. bfRefresh |= UPDATE_CONTEXT | UPDATE_ALLDBGWIN | UPDATE_SYMBOLS_CHANGED; } // update status line SetPidTid_StatusBar(LppdCur, LptdCur); // Update the users view of the world to reflect the last debug // event. if ((bfRefresh != UPDATE_NONE) && (LptdCur != NULL) && (!LptdCur->fInFuncEval)) { UpdateDebuggerState(bfRefresh); } // Worry about executing the next command on the list if (fExecNext && (LptdCur == NULL || LptdCur->fInFuncEval == FALSE) ) { if (CmdExecuteLine(NULL)) { if ((AutoRun == arSource || AutoRun == arCmdline) && !FAutoRunSuppress ) { PostMessage(Views[cmdView].hwndClient, WU_AUTORUN, 0, 0); } else { CmdDoPrompt(!FCmdDoingInput, FALSE); } } else { fGetFocus = FALSE; } } // Grab focus if we really stopped somewhere. if (fGetFocus) { HEXE hexe; ADDR Addr; if (LppdCur && LptdCur) { OSDGetAddr(LppdCur->hpid, LptdCur->htid, adrPC, &Addr); SHChangeProcess(LppdCur->hpds); if ( (HPID)emiAddr( Addr ) == LppdCur->hpid ) { // Get right EMI and load symbols if defered. emiAddr( Addr ) = 0; OSDSetEmi(LppdCur->hpid,LptdCur->htid,&Addr); } hexe = (HEXE)emiAddr( Addr ); if ( hexe && (HPID)hexe != LppdCur->hpid ) { SHWantSymbols( hexe ); } } if (!RemoteRunning) { EnsureFocusDebugger(); } } } } __except(GenericExceptionFilter(GetExceptionInformation())) { //DAssert(FALSE); } fRecurse = FALSE; return(bfRefresh); } /* CmdExecNext() */ BOOL GetAutoRunSuppress( void ) { return FAutoRunSuppress; } BOOL SetAutoRunSuppress( BOOL f ) { BOOL ff = FAutoRunSuppress; FAutoRunSuppress = f; return ff; } /* Hotkey handler ^C is used for a general interrupt signal. When a ^C is caught, a list of handlers is walked. Each handler is executed, and the return value checked. If the return value is TRUE, the walk continues. If the value is FALSE, the walk is terminated. The list is walked in MRU order; the most recently registered handler is called first. */ PCTRLC_HANDLER AddCtrlCHandler( CTRLC_HANDLER_PROC pfnFunc, DWORD dwParam ) /*++ Routine Description: Add a CtrlC handler routine to the handler list. Arguments: pfnFunc - Supplies pointer to handler function dwParam - Supplies parameter to pass to function Return Value: A pointer to the registered handler. This is only used for removing the handler from the chain. --*/ { PCTRLC_HANDLER pcch = (PCTRLC_HANDLER) malloc(sizeof(CTRLC_HANDLER)); if (pcch) { pcch->pfnFunc = pfnFunc; pcch->dwParam = dwParam; pcch->next = PcchHead; PcchHead = pcch; } return pcch; } BOOL RemoveCtrlCHandler( PCTRLC_HANDLER pcch ) /*++ Routine Description: Remove a CtrlC handler from the list. This can remove a specific handler, or remove the last one added. Arguments: pcch - Supplies pointer to the handler to remove. If this is NULL, the last one added will be removed. Return Value: TRUE if the handler was removed, FALSE if it did not exist. --*/ { PCTRLC_HANDLER *ppcch; if (!pcch) { pcch = PcchHead; } for ( ppcch = &PcchHead; *ppcch; ppcch = &((*ppcch)->next) ) { if (*ppcch == pcch) { *ppcch = pcch->next; free(pcch); return TRUE; } } return FALSE; } VOID DispatchCtrlCEvent( VOID ) /*++ Routine Description: Walk the list of Ctrl C handlers, calling each one until one returns FALSE. Arguments: None Return Value: None --*/ { PCTRLC_HANDLER pcch; for (pcch = PcchHead; pcch; pcch = pcch->next) { if (!(*pcch->pfnFunc)(pcch->dwParam)) { break; } } } /* */ BOOL DoCtrlCAsyncStop( DWORD dwParam ) { CmdInsertInit(); CmdLogFmt("Ctrl+C \r\n"); AsyncStop(); return dwParam; } BOOL PolledCtrlCHandler( DWORD dwParam ) { CmdInsertInit(); CmdLogFmt("^C\r\n"); fCtrlCPressed = TRUE; return dwParam; } void SetCtrlCTrap( void ) /*++ Routine Description: This manages a polled Ctrl C trap. Add a handler which sets a flag. Arguments: None Return Value: None --*/ { fCtrlCPressed = FALSE; pPolledCtrlCHandler = AddCtrlCHandler(PolledCtrlCHandler, FALSE); } void ClearCtrlCTrap( void ) /*++ Routine Description: Clear trap for polled CTRL-C Arguments: None Return Value: None --*/ { if (pPolledCtrlCHandler) { RemoveCtrlCHandler(pPolledCtrlCHandler); pPolledCtrlCHandler = NULL; } fCtrlCPressed = FALSE; } ULONG WDBGAPI CheckCtrlCTrap( void ) /*++ Routine Description: Allow any hotkey events to be handled, then check the POLLED Ctrl C flag. If the Ctrl C polling handler is not in use, this will return FALSE and not drain the message queue. Arguments: None Return Value: TRUE if CTRL-C was pressed since last checked. --*/ { BOOL f; MSG msg; BOOL fIgnore; if (!pPolledCtrlCHandler) { return FALSE; } if (cmdView == -1) { return FALSE; } //while (PeekMessage(&msg, NULL, WM_HOTKEY, WM_HOTKEY, PM_REMOVE)) while (PeekMessage(&msg, Views[cmdView].hwndClient, WM_KEYDOWN, WM_DEADCHAR, PM_REMOVE) || PeekMessage(&msg, Views[cmdView].hwndClient, WM_COMMAND, WM_COMMAND, PM_REMOVE)) { switch (msg.message) { default: fIgnore = FALSE; break; case WM_KEYDOWN: case WM_KEYUP: switch (msg.wParam) { default: fIgnore = TRUE; break; case 'C': case VK_CONTROL: case VK_RCONTROL: case VK_LCONTROL: fIgnore = FALSE; break; } break; case WM_CHAR: switch (msg.wParam) { default: fIgnore = TRUE; break; case 'C': case VK_CONTROL: case VK_RCONTROL: case VK_LCONTROL: fIgnore = FALSE; break; } break; } if (!fIgnore) { ProcessQCQPMessage(&msg); } } f = fCtrlCPressed; fCtrlCPressed = FALSE; return f; } BOOL CmdSetEatEOLWhitespace( BOOL ff ) /*++ Routine Description: Set value of EatEOLWhitespace flag to control behaviour of data entry in command window. Arguments: ff - Supplies new value for fEatEOLWhitespace Return Value: Old value of fEatEOLWhitespace --*/ { BOOL f = fEatEOLWhitespace; fEatEOLWhitespace = ff; return f; } void CmdSetAutoHistOK( BOOL f ) { FAutoHistOK = f; } BOOL CmdGetAutoHistOK( void ) { return FAutoHistOK; } LRESULT CmdEditProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) /*++ Routine Description: This function is the window message processor for the command window class. It processes those messages which are of interest to this specific window class and passes all other messages on to the default MDI window procedure handler Arguments: hwnd - Supplies window handle to the command window msg - Supplies message to be processed wParam - Supplies info about the message lParam - Supplies info about the message Return Value: various --*/ { int i; int x; LRESULT lRet = 0; int XPos, YPos; int Xro, Yro; BOOL fShift; BOOL fCtrl; long first; LPLINEREC pl; LPBLOCKDEF pb; char szStr[MAX_LINE_SIZE]; int doc = Views[cmdView].Doc; int nLines; BOOL fDoHist; static BOOL fShowingLine = FALSE; static BOOL fEdited = FALSE; static int nHistoryLine = 0; static int nHistBufTop = 0; // oldest command in history static int nHistBufBot = 0; // where next one goes static LPSTR alpszHistory[MAX_CMDWIN_HISTORY] = {0}; switch (msg) { case WU_INITDEBUGWIN: /* * set up ctrlc handler */ AddCtrlCHandler(DoCtrlCAsyncStop, FALSE); /* ** Initialize cmd processor, show initial prompt. */ CmdSetDefaultCmdProc(); if (!AutoRun) { CmdDoPrompt(TRUE, TRUE); GetRORegion(cmdView, &Xro, &Yro); PosXY(cmdView, Xro, Yro, FALSE); } return FALSE; case WM_SETFOCUS: InterlockedExchangePointer((PVOID *) &hCurrAccTable, hCmdWinAccTable); #if 0 // Under development { // Set the context sensitive menu HMENU hmenuCurr = GetMenu(hwndFrame); if (hmenuCurr != hmenuCmdWin) { SendMessage(g_hwndMDIClient, WM_MDISETMENU, (WPARAM) hwndFrame, (LPARAM) hmenuCmdWin); Assert(DrawMenuBar(hwndFrame)); } } #endif break; case WM_KILLFOCUS: InterlockedExchangePointer((PVOID *) &hCurrAccTable, hMainAccTable); break; case WM_KEYDOWN: fShift = (GetKeyState(VK_SHIFT) < 0); fCtrl = (GetKeyState(VK_CONTROL) < 0); GetRORegion(cmdView, &Xro, &Yro); XPos = Views[cmdView].X; YPos = Views[cmdView].Y; nLines = max(Docs[doc].NbLines, 1); if (YPos >= Yro && XPos >= Xro) { if (YPos > Yro) { Xro = 0; } // In writeable region switch (wParam) { case 'R': if (fCtrl) { // hack... // this is just a macro for ".resync" InsertBlock(doc, Xro, YPos, 7, ".resync"); InvalidateLines(cmdView, nLines-1, nLines-1, TRUE); PosXY(cmdView, Xro + 7, nLines-1, FALSE); lRet = 0; break; } default: fEdited = TRUE; lRet = CallWindowProc(lpfnEditProc, hwnd, msg, wParam, lParam); break; case VK_UP: lRet = 0; if (fCtrl) { // Execute magic ctrl-up KeyDown(cmdView, wParam, fShift, FALSE); } else { // get previous history line, if any: if (!fShowingLine) { // up from new line; show last cmd goto GotHistLine; } else if (nHistoryLine != nHistBufTop) { if (--nHistoryLine < 0) { nHistoryLine = MAX_CMDWIN_HISTORY-1; } goto GotHistLine; } /* else don't do anything */ } lRet = 0; break; case VK_DOWN: // get next history line lRet = 0; i = nHistoryLine; if (i != nHistBufBot && ++i >= MAX_CMDWIN_HISTORY) { i = 0; } if (i != nHistBufBot) { nHistoryLine = i; } else { // no more history; forget it. break; } GotHistLine: // is history empty? if (!alpszHistory[nHistoryLine]) { break; } fShowingLine = TRUE; fEdited = FALSE; // erase command line... DeleteBlock(doc, Xro, nLines - 1, MAX_USER_LINE, nLines - 1); // insert new line: InsertBlock(doc, Xro, nLines-1, strlen(alpszHistory[nHistoryLine]), alpszHistory[nHistoryLine]); InvalidateLines(cmdView, nLines-1, nLines-1, TRUE); PosXY(cmdView, Xro + strlen(alpszHistory[nHistoryLine]), nLines-1, FALSE); break; case VK_LEFT: lRet = 0; if (XPos > Xro) { lRet = CallWindowProc( lpfnEditProc, hwnd, msg, wParam, lParam ); } else if (fCtrl) { KeyDown(cmdView, wParam, fShift, FALSE); } break; case VK_BACK: lRet = 0; if (XPos > Xro) { fEdited = TRUE; lRet = CallWindowProc( lpfnEditProc, hwnd, msg, wParam, lParam ); } break; case VK_HOME: if (fCtrl && fShift || fCtrl) { // Ctrl+Shift+Home or Ctrl+Home pressed lRet = CallWindowProc( lpfnEditProc, hwnd, msg, wParam, lParam ); }if (fShift) { // Shift+Home pressed // The farthest position to the left that can be selected is 2 int tmp_int = FirstNonBlank(doc, YPos); int X = max(tmp_int, 2); if (XPos != X) { SelPosXY(cmdView, X, YPos); } else { SelPosXY(cmdView, 2, YPos); } } else { // Home pressed PosXY(cmdView, Xro, nLines-1, FALSE); } break; case VK_ESCAPE: // erase command line... DeleteBlock(doc, Xro, nLines - 1, MAX_USER_LINE, nLines - 1); PosXY(cmdView, Xro, nLines - 1, FALSE); InvalidateLines(cmdView, nLines-1, nLines-1, TRUE); fShowingLine = FALSE; fEdited = TRUE; break; case VK_RETURN: first = YPos; FirstLine(doc, &pl, &first, &pb); if (!fEatEOLWhitespace) { x = pl->Length - LHD; } else { // put cursor after last non-white char on line // ExpandTabs() expands the string into global // el[] and remembers the length in elLen. ExpandTabs(&pl); x = elLen-1; while (x > -1) { if (isspace(el[x])) { --x; } else { break; } } x++; } x = max(x, Xro); if (!g_contGlobalPreferences_WkSp.m_bCommandRepeat || !CmdGetAutoHistOK() || x != Xro) { PosXY(cmdView, x, YPos, FALSE); fDoHist = TRUE; } else { fDoHist = FALSE; if (!alpszHistory[nHistoryLine]) { *szStr = 0; } else { strcpy(szStr, alpszHistory[nHistoryLine]); if (tolower(*szStr) == 'g' || tolower(*szStr) == '.' || tolower(*szStr) == 'l') { *szStr = 0; } else { InsertBlock(doc, Xro, YPos, strlen(alpszHistory[nHistoryLine]), alpszHistory[nHistoryLine]); InvalidateLines(cmdView, nLines-1, nLines-1, TRUE); PosXY(cmdView, Xro + strlen(alpszHistory[nHistoryLine]), YPos, FALSE); } } } // give user visual feedback SetCaret(cmdView, 0, YPos, -1); UpdateWindow(hwnd); // give CR to edit mangler lRet = CallWindowProc( lpfnEditProc, hwnd, msg, wParam, lParam ); // the line we just entered (reload it, it may have been mangled): first = YPos; FirstLine(doc, &pl, &first, &pb); if (fDoHist) { strncpy(szStr, pl->Text + Xro, x - Xro); szStr[x - Xro] = 0; // remember history // this version remembers everything in order, // but still only resets the history line when // it has been edited. if (*szStr) { if (alpszHistory[nHistBufBot]) { Assert(nHistBufBot == nHistBufTop); free(alpszHistory[nHistBufTop++]); if (nHistBufTop >= MAX_CMDWIN_HISTORY) { nHistBufTop = 0; } } if (!fShowingLine || fEdited) { nHistoryLine = nHistBufBot; } alpszHistory[nHistBufBot++] = _strdup(szStr); if (nHistBufBot >= MAX_CMDWIN_HISTORY) { nHistBufBot = 0; } } } fShowingLine = FALSE; CmdFileString(szStr); CmdFileString("\r\n"); SendClientOutput(szStr, strlen(szStr)); SendClientOutput("\r\n", 2); CmdDoLine(szStr); CmdDoPrompt(TRUE, TRUE); lRet = 0; break; } } else { // in readonly region // everything has to work here... the edit mangler will protect // the readonly region, so we can just throw everything at it, but // we need to decide when to fall onto the command line, and when // to leave things alone... first = nLines - 1; FirstLine(doc, &pl, &first, &pb); if (wParam == CTRL_M) { // position at end of cmd line ClearSelection(cmdView); PosXY(cmdView, pl->Length-LHD, nLines - 1, FALSE); lRet = 0; } else { EnableReadOnlyBeep(FALSE); lRet = CallWindowProc(lpfnEditProc, hwnd, msg, wParam, lParam); if (QueryReadOnlyError()) { // position at end of command line, and try again ClearSelection(cmdView); PosXY(cmdView, pl->Length-LHD, nLines - 1, FALSE); lRet = CallWindowProc(lpfnEditProc, hwnd, msg, wParam, lParam); } EnableReadOnlyBeep(TRUE); } } return lRet; case WM_IME_REPORT: if (IR_STRING != wParam) { break; } // Fall through case WM_CHAR: // the interesting cases have already been handled; we just want // to fall onto the command line in case of an error if (wParam == CTRL_R) { return 0; } EnableReadOnlyBeep(FALSE); lRet = CallWindowProc( lpfnEditProc, hwnd, msg, wParam, lParam ); if (QueryReadOnlyError()) { // position at end of command line, and try again ClearSelection(cmdView); nLines = max(Docs[doc].NbLines, 1); first = nLines - 1; FirstLine(doc, &pl, &first, &pb); PosXY(cmdView, pl->Length-LHD, nLines - 1, FALSE); lRet = CallWindowProc( lpfnEditProc, hwnd, msg, wParam, lParam ); } EnableReadOnlyBeep(TRUE); return lRet; case WM_PASTE: if (OpenClipboard(hwndFrame)) { HANDLE hData; size_t size; LPSTR p1; LPSTR p; hData = GetClipboardData(CF_TEXT); if (hData && (size = GlobalSize (hData))) { if (size >= MAX_CLIPBOARD_SIZE) { ErrorBox(ERR_Clipboard_Overflow); } else if ( p = (PSTR) GlobalLock(hData) ) { int x, y; x = Views[cmdView].X; y = Views[cmdView].Y; p1 = p; while (size && *p1) { size--; if (IsDBCSLeadByte(*p1) && *(p1+1)) { p1 += 2; size--; continue; } if (*p1 == '\r' || *p1 == '\n') { break; } p1++; } size = (size_t) (p1 - p); InsertStream(cmdView, x, y, size, p, TRUE); PosXY(cmdView, x + size, y, TRUE); DbgX(GlobalUnlock (hData) == FALSE); } CloseClipboard(); } } return 0; case WM_DESTROY: /* ** Destroy this instance of the window proc */ //UnregisterHotKey(NULL, IDH_CTRLC); UnregisterHotKey(hwnd, IDH_CTRLC); while (RemoveCtrlCHandler(NULL)) { ; } FreeProcInstance((WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (DWORD_PTR)lpfnEditProc)); break; case WU_AUTORUN: /* ** Need to get and process an auto-run command */ CmdAutoRunNext(); break; case WU_LOG_REMOTE_CMD: // Echo and handle a command sent by a remote client if (wParam) { CmdLogFmtEx( TRUE, FALSE, TRUE, (LPSTR)lParam); } CmdDoLine( (LPSTR)lParam ); CmdDoPrompt(!FCmdDoingInput, TRUE); FCmdDoingInput = FALSE; if (wParam) { free((LPVOID)lParam); } break; case WU_LOG_REMOTE_MSG: // print some random junk from a remote client CmdInsertInit(); if (!wParam) { GetRORegion(cmdView, &Xro, &Yro); first = Yro; FirstLine(doc, &pl, &first, &pb); strncpy(szStr, pl->Text, Xro); szStr[Xro] = 0; CmdLogDebugString(szStr, FALSE); } CmdLogDebugString( (LPSTR)lParam, TRUE); free( (LPSTR)lParam ); break; } return ( CallWindowProc( lpfnEditProc, hwnd, msg, wParam, lParam ) ); } /* CmdEditProc() */