|
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