} // end InterceptCtrlC() protected override void ProcessRecord() { if (!DbgProvider.IsInGuestMode) { throw new InvalidOperationException("This command is only intended to be used by the !dbgshell implementation."); } LogManager.Trace("WaitForBangDbgShellCommandCommand.ProcessRecord: DbgShell is going dormant."); using (var disposer = new ExceptionGuard()) { disposer.Protect(SetDebuggerAndContextAndUpdateCallbacks()); disposer.Protect(InterceptCtrlC()); disposer.Protect(Debugger.SuspendEventOutput()); // don't want our "modload" output etc. spewing while we are dormant base.ProcessRecord(); // We need to perform the wait on another thread, because we need to "pump" // on this thread, so that debug event callbacks can queue WriteObject calls // to this thread. _RegisterWaitForGuestModeEvent(disposer); // Give the user a hint about what's going on. var s = new ColorString(ConsoleColor.DarkGray, "(DbgShell will remain running in the background until you run !dbgshell again)").ToString(true); Host.UI.WriteLine(s); MsgLoop.Prepare(); // This will allow the extension command to return (it tells the dbgeng // thread that it can return). DbgProvider.GuestModePassivate(); MsgLoop.Run(); // This will complete when the guest mode event gets signaled. } // end using( disposer ) } // end ProcessRecord()
private void OnOpenStore() { var selection = this.GetSingleOrDefaultSelection(); if (selection == null) { return; } if (selection.SignatureKind != SignatureKind.Store) { return; } var link = $"ms-windows-store://pdp/?PFN={selection.PackageFamilyName}"; var psi = new ProcessStartInfo(link) { UseShellExecute = true }; ExceptionGuard.Guard ( () => { Process.Start(psi); }, this.interactionService ); }
public void Convert() { var tempReg = System.Guid.NewGuid().ToString("N") + ".reg"; var tempDir = "reg-out-" + System.Guid.NewGuid().ToString("N"); Directory.CreateDirectory(tempDir); try { var mfn = typeof(TestConversion).Assembly.GetManifestResourceNames().First(a => a.EndsWith("team.reg")); using (var mf = typeof(TestConversion).Assembly.GetManifestResourceStream(mfn)) { using (var fs = File.OpenWrite(tempReg)) { // ReSharper disable once PossibleNullReferenceException mf.CopyTo(fs); fs.Flush(true); } } var regConv = new MsixRegistryFileWriter(tempDir); var regPars = new RegFileParser(); regPars.Parse(Path.Combine(tempReg)); regConv.ImportRegFile(tempReg); } finally { ExceptionGuard.Guard(() => { Directory.Delete(tempDir, true); File.Delete(tempReg); }); } }
public async Task <string> GetSubjectFromDeviceGuardSigning(string accessToken, string refreshToken, CancellationToken cancellationToken = default) { string tempFile = null; try { var cfg = new DeviceGuardConfig { AccessToken = accessToken, RefreshToken = refreshToken }; var helper = new DgssTokenCreator(); tempFile = await helper.CreateDeviceGuardJsonTokenFile(cfg, cancellationToken).ConfigureAwait(false); return(await this.GetSubjectFromDeviceGuardSigning(tempFile, cancellationToken).ConfigureAwait(false)); } finally { if (tempFile != null && File.Exists(tempFile)) { ExceptionGuard.Guard(() => File.Delete(tempFile)); } } }
public async Task MountVhd(string vhdPath, CancellationToken cancellationToken = default, IProgress <ProgressData> progress = null) { Logger.Info($"Mounting volume {vhdPath}..."); var tempFile = Path.Combine(Path.GetTempPath(), "msix-hero-vhd-" + Guid.NewGuid().ToString("N").Substring(0, 10) + ".cfg"); try { var content = @"select vdisk file = ""{0}"" attach vdisk"; await File.WriteAllTextAsync(tempFile, string.Format(content, vhdPath), cancellationToken).ConfigureAwait(false); var arguments = $"/S \"{tempFile}\""; Logger.Debug($"DISKPART.EXE command in {tempFile}:\r\n{string.Format(content, vhdPath)}"); await this.RunDiskPart(arguments, cancellationToken).ConfigureAwait(false); } finally { if (File.Exists(tempFile)) { Logger.Debug($"Deleting {tempFile}..."); ExceptionGuard.Guard(() => File.Delete(tempFile)); } } }
// ReSharper disable once UnusedParameter.Local private void Dispose(bool disposing) { if (this.tempDirectory?.Exists == true) { ExceptionGuard.Guard(() => this.tempDirectory.Delete(true)); this.tempDirectory = null; } }
private void Hyperlink_OnClick(object sender, RoutedEventArgs e) { ExceptionGuard.Guard(() => { var dir = (string)((Hyperlink)sender).Tag; Process.Start("explorer.exe", "/select," + Path.Combine(dir, FileConstants.AppxManifestFile)); }, new InteractionService(new NotificationManager())); }
// Wait for the "guest mode" event to be signaled. private void _RegisterWaitForGuestModeEvent(ExceptionGuard disposer) { LogManager.Trace("Registering wait for GuestMode event."); ThreadPool.RegisterWaitForSingleObject(DbgProvider.GuestModeEvent, (x, y) => { disposer.Dispose(); SignalDone(); }, null, // state -1, // INFINITE true); // executeOnlyOnce } // end _RegisterWaitForGuestModeEvent()
private void OnOpenReleaseNotes() { ExceptionGuard.Guard(() => { var psi = new ProcessStartInfo("https://msixhero.net/redirect/release-notes/" + this.CurrentVersion) { UseShellExecute = true }; Process.Start(psi); }, this.interactionService); }
private void HyperlinkOnClick(object sender, RoutedEventArgs e) { ExceptionGuard.Guard(() => { var psi = new ProcessStartInfo((string)((Hyperlink)sender).Tag) { UseShellExecute = true }; Process.Start(psi); }, new InteractionService(new NotificationManager())); }
public async Task Should_return_null_response_if_exception_was_caught( Exception exception) { var logger = Substitute.For <IInternalLogger>(); async Task <Verdict> DoRequest() { return(await Task.FromException <Verdict>(exception)); } var result = await ExceptionGuard.Try(DoRequest, logger); result.Should().BeNull(); }
public async Task Should_log_exceptions_as_errors( Exception exception) { var logger = Substitute.For <IInternalLogger>(); async Task <Verdict> DoRequest() { return(await Task.FromException <Verdict>(exception)); } await ExceptionGuard.Try(DoRequest, logger); logger.Received().Error(exception.ToString); }
public async Task Should_not_rethrow_non_external_exceptions( Exception exception) { var logger = Substitute.For <IInternalLogger>(); async Task <Verdict> DoRequest() { return(await Task.FromException <Verdict>(exception)); } Func <Task> act = async() => await ExceptionGuard.Try(DoRequest, logger); await act.Should().NotThrowAsync <Exception>(); }
static App() { var logLevel = MsixHeroLogLevel.Info; ExceptionGuard.Guard(() => { var service = new LocalConfigurationService(); var config = service.GetCurrentConfiguration(); logLevel = config.VerboseLogging ? MsixHeroLogLevel.Trace : MsixHeroLogLevel.Info; }); LogManager.Initialize(logLevel); }
private async void OnView() { var selectedFile = this.SelectedNode; if (selectedFile == null) { return; } using var reader = FileReaderFactory.CreateFileReader(this.PackageFile); var path = await this.fileViewer.GetDiskPath(reader, selectedFile.Path).ConfigureAwait(false); ExceptionGuard.Guard(() => { this.fileInvoker.Execute(path, true); }); }
public async Task Should_return_request_response_if_no_exception_is_thrown( Verdict response) { var logger = Substitute.For <IInternalLogger>(); async Task <Verdict> DoRequest() { return(await Task.FromResult(response)); } var result = await ExceptionGuard.Try(DoRequest, logger); result.Should().Be(response); }
public async Task<string> GetSubjectFromDeviceGuardSigning(string dgssTokenPath, CancellationToken cancellationToken = default) { Logger.Info("Getting certificate subject for Device Guard signing..."); var tempFilePath = Path.Combine(Path.GetTempPath(), "msix-hero-" + Guid.NewGuid().ToString("N") + ".cat"); try { var name = typeof(DeviceGuardHelper).Assembly.GetManifestResourceNames().First(n => n.EndsWith("MSIXHeroTest.cat")); using (var manifestResourceStream = typeof(DeviceGuardHelper).Assembly.GetManifestResourceStream(name)) { if (manifestResourceStream == null) { throw new InvalidOperationException("Cannot extract temporary file."); } Logger.Debug($"Creating temporary file path {tempFilePath}"); using (var fileStream = File.Create(tempFilePath)) { manifestResourceStream.Seek(0L, SeekOrigin.Begin); await manifestResourceStream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); } } var sdk = new MsixSdkWrapper(); Logger.Debug($"Signing temporary file path {tempFilePath}"); await sdk.SignPackageWithDeviceGuard(new[] {tempFilePath}, "SHA256", dgssTokenPath, null, cancellationToken).ConfigureAwait(false); using (var fromSignedFile = X509Certificate.CreateFromSignedFile(tempFilePath)) { Logger.Info($"Certificate subject is {tempFilePath}"); return fromSignedFile.Subject; } } catch (Exception e) { Logger.Error("Could not read subject from Device Guard certificate.", e); throw; } finally { if (File.Exists(tempFilePath)) { Logger.Debug($"Removing {tempFilePath}"); ExceptionGuard.Guard(() => File.Delete(tempFilePath)); } } }
static Program() { AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException; TaskScheduler.UnobservedTaskException += TaskSchedulerOnUnobservedTaskException; var logLevel = MsixHeroLogLevel.Info; ExceptionGuard.Guard(() => { var service = new LocalConfigurationService(); var config = service.GetCurrentConfiguration(); logLevel = config.VerboseLogging ? MsixHeroLogLevel.Trace : MsixHeroLogLevel.Info; }); LogManager.Initialize(logLevel); }
private async Task <int> SignDeviceGuardInteractive(string timestamp, bool updatePublisherName) { string json = null; DeviceGuardConfig cfg; try { cfg = await this.DeviceGuardTokenCreator.SignIn().ConfigureAwait(false); json = await this.DeviceGuardTokenCreator.CreateDeviceGuardJsonTokenFile(cfg).ConfigureAwait(false); if (!this.Verb.NoPublisherUpdate) { await this.Console.WriteInfo("Determining publisher name from Device Guard Signing certificate...").ConfigureAwait(false); cfg.Subject = await this.DeviceGuardHelper.GetSubjectFromDeviceGuardSigning(json).ConfigureAwait(false); await this.Console.WriteSuccess("New publisher name is " + cfg.Subject).ConfigureAwait(false); } } catch (SdkException e) { Logger.Error(e); await this.Console.WriteError($"Signing failed with error code 0x{e.ExitCode:X}: {e.Message}"); return(e.ExitCode); } catch (Exception e) { Logger.Error(e); await this.Console.WriteError($"Signing failed: {e.Message}"); return(10); } finally { if (json != null && File.Exists(json)) { ExceptionGuard.Guard(() => File.Delete(json)); } } return(await this.SignDeviceGuard(cfg, timestamp, updatePublisherName).ConfigureAwait(false)); }
} // end BeginProcessing() protected override void ProcessRecord() { var inputCallbacks = Debugger.GetInputCallbacks() as DebugInputCallbacks; if (null != inputCallbacks) { inputCallbacks.UpdateCmdlet(this); } //var outputCallbacks = Debugger.GetOutputCallbacks() as DebugOutputCallbacks; //if( null != outputCallbacks ) // outputCallbacks.UpdateCmdlet( this ); using (var disposer = new ExceptionGuard()) { disposer.Protect(Debugger.SetCurrentCmdlet(this)); disposer.Protect(InterceptCtrlC()); MsgLoop.Prepare(); string actualCommand = string.Join(" ", Command); Task t = Debugger.InvokeDbgEngCommandAsync(actualCommand, OutputPrefix, _ConsumeLine); Task t2 = t.ContinueWith(async(x) => { DEBUG_STATUS execStatus = Debugger.GetExecutionStatus(); if (_StatusRequiresWait(execStatus)) { LogManager.Trace("InvokeDbgExtensionCommand: Current execution status ({0}) indicates that we should enter a wait.", execStatus); await WaitForBreakAsync(); } disposer.Dispose(); SignalDone(); }).Unwrap(); MsgLoop.Run(); Host.UI.WriteLine(); Util.Await(t); // in case it threw Util.Await(t2); // in case it threw } // end using( disposer ) } // end ProcessRecord()
private void OpenLogsClicked(object sender, RoutedEventArgs e) { var logFile = LogManager.LogFile; if (logFile != null) { ExceptionGuard.Guard(() => { var psi = new ProcessStartInfo { UseShellExecute = true, FileName = "explorer.exe", Arguments = "/e," + Path.GetDirectoryName(logFile) }; Process.Start(psi); }); } }
protected override void ProcessRecord() { base.ProcessRecord(); using (var disposer = new ExceptionGuard(Debugger.HandleDbgEngOutput(_ConsumeLine))) { // Failure to load an extension should be a terminating error. // // Because the only reason to load an extension is so that you can call // commands in it. And if you didn't load it, then none of those commands // will work. string providerPath = PathOrName; if (providerPath.IndexOfAny(sm_dirSeparators) >= 0) { // We support PS paths. // // (we don't want to call GetUnresolvedProviderPathFromPSPath if it's // just a bare name, else PS will interpret that as a relative path, // which would be wrong) providerPath = GetUnresolvedProviderPathFromPSPath(PathOrName); } MsgLoop.Prepare(); var t = Debugger.AddExtensionAsync(providerPath); t.ContinueWith((x) => { disposer.Dispose(); SignalDone(); }); MsgLoop.Run(); var elr = Util.Await(t); // in case it threw if (!string.IsNullOrEmpty(elr.LoadOutput)) { SafeWriteObject(elr.LoadOutput); } SafeWriteVerbose("Loaded extension: {0}", PathOrName); } // end using( disposer ) } // end ProcessRecord()
public override async Task <int> Execute() { var msixSdkWrapper = new MakeAppxWrapper(); Logger.Info($"Packing [{this.Verb.Directory}] to [{this.Verb.Package}]..."); try { await this.Console.WriteInfo($"Packing [{this.Verb.Directory}] to [{this.Verb.Package}]...").ConfigureAwait(false); await msixSdkWrapper.PackPackageDirectory(this.Verb.Directory, this.Verb.Package, !this.Verb.NoCompression, !this.Verb.NoValidation).ConfigureAwait(false); await this.Console.WriteSuccess($"Package [{this.Verb.Package}] has been created."); if (this.Verb.RemoveDirectoryAfterPacking) { await this.Console.WriteInfo($"Removing source directory {this.Verb.Directory}..."); ExceptionGuard.Guard(() => Directory.Delete(this.Verb.Directory, true)); } return(StandardExitCodes.ErrorSuccess); } catch (SdkException e) { Logger.Error(e); await this.Console.WriteError(e.Message); return(e.ExitCode); } catch (Exception e) { Logger.Error(e); await this.Console.WriteError(e.Message); return(StandardExitCodes.ErrorGeneric); } }
public override async Task <int> Execute() { var msixSdkWrapper = new MakeAppxWrapper(); Logger.Info($"Unpacking [{this.Verb.Package}] to [{this.Verb.Directory}]..."); try { await this.Console.WriteInfo($"Unpacking [{this.Verb.Package}] to [{this.Verb.Directory}]...").ConfigureAwait(false); await msixSdkWrapper.UnpackPackage(this.Verb.Package, this.Verb.Directory, !this.Verb.NoValidation).ConfigureAwait(false); await this.Console.WriteSuccess($"The package has been unpacked to [{this.Verb.Directory}]."); if (this.Verb.RemovePackageAfterExtraction) { await this.Console.WriteInfo($"Removing source package {this.Verb.Package}..."); ExceptionGuard.Guard(() => File.Delete(this.Verb.Package)); } return(StandardExitCodes.ErrorSuccess); } catch (SdkException e) { Logger.Error(e); await this.Console.WriteError(e.Message); return(e.ExitCode); } catch (Exception e) { Logger.Error(e); await this.Console.WriteError(e.Message); return(StandardExitCodes.ErrorGeneric); } }
static async Task <int> Main(string[] args) { var logLevel = MsixHeroLogLevel.Trace; ExceptionGuard.Guard(() => { var service = new LocalConfigurationService(); var config = service.GetCurrentConfiguration(); logLevel = config.VerboseLogging ? MsixHeroLogLevel.Trace : MsixHeroLogLevel.Info; }); LogManager.Initialize(logLevel); var console = new ConsoleImpl(Console.Out, Console.Error); try { // First argument is the "edit" verb. // Second argument is the path of the package. if (args.Length > 1 && string.Equals(args[0], "edit", StringComparison.OrdinalIgnoreCase) && !args[1].StartsWith("-", StringComparison.OrdinalIgnoreCase)) { return(await DoEditVerb(args[1], args.Skip(2), console)); } return(await DoCommonVerbs(args, console)); } catch (Exception e) { await console.WriteError(e.Message).ConfigureAwait(false); Environment.ExitCode = 1; return(1); } }
public void Convert() { var tempReg = System.Guid.NewGuid().ToString("N") + ".reg"; var tempDir = "reg-out-" + System.Guid.NewGuid().ToString("N"); Directory.CreateDirectory(tempDir); try { var mfn = typeof(TestConversion).Assembly.GetManifestResourceNames().First(a => a.EndsWith("team.reg")); using (var mf = typeof(TestConversion).Assembly.GetManifestResourceStream(mfn)) { using (var fs = System.IO.File.OpenWrite(tempReg)) { // ReSharper disable once PossibleNullReferenceException mf.CopyTo(fs); fs.Flush(true); } } var regConv = new RegConverter(); var regPars = new RegFileParser(); regPars.Parse(Path.Combine(tempReg)); regConv.ConvertFromRegToDat(tempReg, Path.Combine(tempDir, "Registry.dat"), RegistryRoot.HKEY_LOCAL_MACHINE).GetAwaiter().GetResult(); regConv.ConvertFromRegToDat(tempReg, Path.Combine(tempDir, "UserClasses.dat"), RegistryRoot.HKEY_CLASSES_ROOT).GetAwaiter().GetResult(); regConv.ConvertFromRegToDat(tempReg, Path.Combine(tempDir, "User.dat"), RegistryRoot.HKEY_CURRENT_USER).GetAwaiter().GetResult(); } finally { ExceptionGuard.Guard(() => { System.IO.Directory.Delete(tempDir, true); System.IO.File.Delete(tempReg); }); } }
private async Task <T> TryRequest <T>(Func <Task <T> > request) where T : class { return(await ExceptionGuard.Try(request, _logger)); }
static int Main(string[] args) { // This allows "[Console]::WriteLine( [char] 0x2026 )" to work just as well as // "[char] 0x2026". Console.OutputEncoding = Encoding.UTF8; // // We've got four possibilities: // // 1) Normal mode: somebody just ran DbgShell.exe. // 2) Guest mode: DbgShell is being hosted by a debugger extension // (DbgShellExt.dll). // 3) Guest mode cleanup: the debugger extension (DbgShellExt) is being // unloaded. // 4) GuestAndHost mode: DbgShell.exe is the host, but !DbgShellExt.dbgshell // was called. // DbgProvider.EntryDepth++; ExceptionGuard entryPointStateDisposer = new ExceptionGuard( (Disposable)(() => LogManager.Trace("Undoing entry point state changes."))); entryPointStateDisposer.Protect((Disposable)(() => DbgProvider.EntryDepth--)); using ( entryPointStateDisposer ) { if ((null != args) && (args.Length > 0) && (0 == StringComparer.Ordinal.Compare(args[0], c_guestMode))) { LogManager.Trace("Main: GuestMode"); // // In guest mode, the entry thread is used as the DbgEng thread, and a // separate thread is started to run our Main. When the user runs "exit", // we don't actually exit--we "pause" the main thread, and then return // from here (returning control back to the debugger (like windbg)). // args = args.Skip(1).ToArray(); return(_GuestModeMainWrapper(entryPointStateDisposer, args)); } else if ((null != args) && (args.Length > 0) && (0 == StringComparer.Ordinal.Compare(args[0], c_guestModeCleanup))) { Util.Assert(args.Length == 1); // there shouldn't be any other args LogManager.Trace("Main: GuestModeCleanup"); // // We're in guest mode and the debugger extension is being unloaded--time // to clean up. // return(_GuestModeCleanup()); } else if ((null != args) && (args.Length > 0) && (0 == StringComparer.Ordinal.Compare(args[0], c_guestAndHostMode))) { LogManager.Trace("Main: GuestAndHostMode"); // // GuestAndHost mode is pretty much just like the "reentrant" case in // GuestMode. Because DbgShell.exe is already running and owns "main" and // everything, we don't use any of the other guest mode machinery besides // the CurrentActionQueue and EntryDepth. // args = args.Skip(1).ToArray(); return(_GuestAndHostModeMainWrapper(entryPointStateDisposer, args)); } else { LogManager.Trace("Main: Normal"); // // "Normal" (non-guest) mode. // return(_NormalModeMainWrapper(args)); } // end else "normal" mode // The stuff protected by the entryPointStateDisposer may not get disposed // here--it may have been transferred to a different thread via // entryPointStateDisposer.Transfer(). } // end using( entryPointStateDisposer ) } // end Main()
} // 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()