} // end _NormalModeMainWrapper() private static int _GuestAndHostModeMainWrapper(ExceptionGuard entryPointStateDisposer, string[] args) { // We should have two things: // args[ 0 ]: "shareConsole" // args[ 1 ]: command args should all should get passed in one big string. // // Unlike plain guest mode, we know we are "sharing" the console (because // DbgShell.exe is the host!), but it's convenient for the caller to still // pass the console ownership arg. Util.Assert(2 == args.Length); if (0 != StringComparer.Ordinal.Compare(args[0], c_guestModeShareConsole)) { var msg = "Unexpected arg: I expected to see \"shareConsole\"."; Util.Fail(msg); throw new Exception(msg); } // // We're getting called on the dbgeng thread, which should be a background // thread, but the API we use from DbgShellExt.dll (RunAssembly) causes this // thread to get marked as non-background, so we need to put that back. // Thread.CurrentThread.IsBackground = true; try { // We'll always pretend it's the first time for the purpose of parsing // arguments--on the one hand, -NoProfile doesn't make sense (it can't // /ever/ make sense in guestAndHostMode; the profile was run or not // when DbgShell.exe started), but we don't want to warn about it. // // TODO: Is that the right decision? bool noProfile; bool inBreakpointCommand; string command = _ParseGuestModeArgs(args[1], /* firstTime = */ true, out noProfile, out inBreakpointCommand); if (inBreakpointCommand && !DbgProvider.IsInBreakpointCommand) { DbgProvider.IsInBreakpointCommand = true; entryPointStateDisposer.Protect((Disposable)(() => DbgProvider.IsInBreakpointCommand = false)); } // Re-entrant case: DbgShell is already running, but something called // the debugger "!dbgshell" extension command (for instance, a // breakpoint command). LogManager.Trace("_GuestAndHostModeMainWrapper: re-entering (entryDepth: {0}).", DbgProvider.EntryDepth); if (null == DbgProvider.CurrentActionQueue) { var msg = "(_GuestAndHostModeMainWrapper) DbgShell is already running, but I don't have a CurrentActionQueue to inject commands."; Util.Fail(msg); throw new Exception(msg); } var transferTicket = entryPointStateDisposer.Transfer(); DbgProvider.CurrentActionQueue.PostCommand((cii) => { // TODO: BUGBUG: I need to do something here to separate CTRL+C // handling for 'command' versus the cmdlet that is running the // CurrentActionQueue. That is, if we post a command here that // does a naked "Get-Content", it will prompt you for a path, and // if you then do a CTRL+C, I want it to only cancel 'command', // not the entire pipeline including the Resume-Exection command // or whatever it is that is running the CurrentActionQueue. // // Besides just to behave as the user wants/expects, it throws a // wrench in things for execution commands to get canceled--it's // very important, so they do their own CTRL+C handling. using (transferTicket.Redeem()) { LogManager.Trace("Executing injected (reentrant) commands:"); LogManager.Trace(command.Replace("\n", "\n > ")); cii.InvokeScript(command, false, PipelineResultTypes.Error | PipelineResultTypes.Output, sm_noInput); } // end using( new disposer ) }); transferTicket.CommitTransfer(); LogManager.Trace("_GuestAndHostModeMainWrapper: returning {0}.", Util.FormatErrorCode(sm_exitCode)); return(sm_exitCode); } finally { LogManager.Trace("_GuestAndHostModeMainWrapper: leaving."); } } // end _GuestAndHostModeMainWrapper()
private static int _GuestModeMainWrapper(ExceptionGuard entryPointStateDisposer, string[] args) { // We should have two things: // args[ 0 ]: either "consoleOwner" or "shareConsole" (windbg versus ntsd, for instance) // args[ 1 ]: command args should all should get passed in one big string. Util.Assert(2 == args.Length); bool shareConsole = false; if (0 == StringComparer.Ordinal.Compare(args[0], c_guestModeShareConsole)) { shareConsole = true; } else { if (0 != StringComparer.Ordinal.Compare(args[0], c_guestModeConsoleOwner)) { var msg = "Unexpected arg: I expected to see \"consoleOwner\"."; Util.Fail(msg); throw new Exception(msg); } } bool firstTime = null == sm_altMainThread; bool alreadyRunning = DbgProvider.EntryDepth > 1; try { bool noProfile; bool inBreakpointCommand; string command = _ParseGuestModeArgs(args[1], firstTime, out noProfile, out inBreakpointCommand); if (inBreakpointCommand && !DbgProvider.IsInBreakpointCommand) { DbgProvider.IsInBreakpointCommand = true; entryPointStateDisposer.Protect((Disposable)(() => DbgProvider.IsInBreakpointCommand = false)); } if (firstTime) { Util.Assert(!alreadyRunning); ColorConsoleHost.GuestModeInjectCommand(command); LogManager.Trace("_GuestModeMainWrapper: initial entry."); // This is the first time we've been run, so we need to start everything // up, pretty much the same as when running as a normal EXE. sm_altMainThread = new Thread(_AltMainThread); DbgProvider.SetGuestMode(shareConsole); string[] mainArgs = null; if (noProfile) { mainArgs = new string[] { "-NoProfile" } } ; sm_altMainThread.Start(mainArgs); // This will get signaled when things have been initialized enough to // where we can start the guest mode thread activity. DbgProvider.GuestModeEvent.WaitOne(); DbgProvider.GuestModeEvent.Reset(); } else { if (alreadyRunning) { // Re-entrant case: DbgShell is already running, but something called // the debugger "!dbgshell" extension command (for instance, a // breakpoint command). LogManager.Trace("_GuestModeMainWrapper: re-entering (entryDepth: {0}).", DbgProvider.EntryDepth); if (null == DbgProvider.CurrentActionQueue) { Util.Fail("DbgShell is already running, but I don't have a CurrentActionQueue to inject commands."); throw new Exception("DbgShell is already running, but I don't have a CurrentActionQueue to inject commands."); } var transferTicket = entryPointStateDisposer.Transfer(); DbgProvider.CurrentActionQueue.PostCommand((cii) => { using (transferTicket.Redeem()) { LogManager.Trace("Executing injected (reentrant) commands:"); LogManager.Trace(command.Replace("\n", "\n > ")); cii.InvokeScript(command, false, PipelineResultTypes.Error | PipelineResultTypes.Output, sm_noInput); } // end using( new disposer ) }); transferTicket.CommitTransfer(); } else { LogManager.Trace("_GuestModeMainWrapper: resuming."); ColorConsoleHost.GuestModeInjectCommand(command); // We already have an alternate main thread, which is "paused" (blocked), // waiting to resume. DbgProvider.GuestModeResume(); } } if (!alreadyRunning) { LogManager.Trace("_GuestModeMainWrapper: entering GuestModeGuestThreadActivity."); // The guest mode thread activity is to pump the DbgEngThread (this thread is // the thread that the debugger extension was called on, and we need to use // this thread to interact with dbgeng). // // (if alreadyRunning, then we are already running the // GuestModeGuestThreadActivity (it's below us on the stack)) DbgProvider.GuestModeGuestThreadActivity(); } LogManager.Trace("_GuestModeMainWrapper: returning {0}.", Util.FormatErrorCode(sm_exitCode)); return(sm_exitCode); } finally { LogManager.Trace("_GuestModeMainWrapper: leaving."); } } // end _GuestModeMainWrapper()