|
6 | 6 | using System.Globalization; |
7 | 7 | using System.IO; |
8 | 8 | using System.Management.Automation.Host; |
| 9 | +using System.Reflection; |
9 | 10 | using System.Text; |
10 | 11 | using System.Threading; |
11 | 12 | using System.Threading.Tasks; |
@@ -39,6 +40,8 @@ internal class PsesInternalHost : PSHost, IHostSupportsInteractiveSession, IRuns |
39 | 40 | private static string CommandsModulePath => Path.GetFullPath(Path.Combine( |
40 | 41 | s_bundledModulePath, "PowerShellEditorServices", "Commands", "PowerShellEditorServices.Commands.psd1")); |
41 | 42 |
|
| 43 | + private static readonly PropertyInfo s_scriptDebuggerTriggerObjectProperty; |
| 44 | + |
42 | 45 | private readonly ILoggerFactory _loggerFactory; |
43 | 46 |
|
44 | 47 | private readonly ILogger _logger; |
@@ -89,6 +92,21 @@ internal class PsesInternalHost : PSHost, IHostSupportsInteractiveSession, IRuns |
89 | 92 |
|
90 | 93 | private bool _resettingRunspace; |
91 | 94 |
|
| 95 | + static PsesInternalHost() |
| 96 | + { |
| 97 | + Type scriptDebuggerType = typeof(PSObject).Assembly |
| 98 | + .GetType("System.Management.Automation.ScriptDebugger"); |
| 99 | + |
| 100 | + if (scriptDebuggerType is null) |
| 101 | + { |
| 102 | + return; |
| 103 | + } |
| 104 | + |
| 105 | + s_scriptDebuggerTriggerObjectProperty = scriptDebuggerType.GetProperty( |
| 106 | + "TriggerObject", |
| 107 | + BindingFlags.Instance | BindingFlags.NonPublic); |
| 108 | + } |
| 109 | + |
92 | 110 | public PsesInternalHost( |
93 | 111 | ILoggerFactory loggerFactory, |
94 | 112 | ILanguageServerFacade languageServer, |
@@ -1142,6 +1160,34 @@ internal void WaitForExternalDebuggerStops() |
1142 | 1160 |
|
1143 | 1161 | private void OnDebuggerStopped(object sender, DebuggerStopEventArgs debuggerStopEventArgs) |
1144 | 1162 | { |
| 1163 | + // If ErrorActionPreference is set to Break, any engine exception is going to trigger a |
| 1164 | + // pipeline stop. Technically this is the same behavior as a standalone PowerShell |
| 1165 | + // process, but we use pipeline stops with greater frequency due to features like run |
| 1166 | + // selection and terminating the debugger. Without this, if the "Stop" button is pressed |
| 1167 | + // then we hit this repeatedly. |
| 1168 | + // |
| 1169 | + // This info is publically accessible via `PSDebugContext` but we'd need to access it |
| 1170 | + // via a script. At this point in the call I'd prefer this to be as light as possible so |
| 1171 | + // we can escape ASAP but we may want to consider switching to that at some point. |
| 1172 | + if (!Runspace.RunspaceIsRemote && s_scriptDebuggerTriggerObjectProperty is not null) |
| 1173 | + { |
| 1174 | + object triggerObject = null; |
| 1175 | + try |
| 1176 | + { |
| 1177 | + triggerObject = s_scriptDebuggerTriggerObjectProperty.GetValue(Runspace.Debugger); |
| 1178 | + } |
| 1179 | + catch |
| 1180 | + { |
| 1181 | + // Ignore all exceptions. There shouldn't be any, but as this is implementation |
| 1182 | + // detail that is subject to change it's best to be overly cautious. |
| 1183 | + } |
| 1184 | + |
| 1185 | + if (triggerObject is PipelineStoppedException pse) |
| 1186 | + { |
| 1187 | + throw pse; |
| 1188 | + } |
| 1189 | + } |
| 1190 | + |
1145 | 1191 | // The debugger has officially started. We use this to later check if we should stop it. |
1146 | 1192 | DebugContext.IsActive = true; |
1147 | 1193 |
|
|
0 commit comments