diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/DebugRunspaceCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/DebugRunspaceCommand.cs index f5d84dfd195..701ba91f857 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/DebugRunspaceCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/DebugRunspaceCommand.cs @@ -265,6 +265,15 @@ private void WaitAndReceiveRunspaceOutput() // Set up host script debugger to debug the runspace. _debugger.DebugRunspace(_runspace, breakAll: BreakAll); + _runspace.IsRemoteDebuggerAttached = true; + _runspace.Events?.GenerateEvent( + PSEngineEvent.OnDebugAttach, + null, + Array.Empty(), + null, + true, + false); + while (_debugging) { // Wait for running script. @@ -307,6 +316,7 @@ private void WaitAndReceiveRunspaceOutput() { _runspace.AvailabilityChanged -= HandleRunspaceAvailabilityChanged; _debugger.NestedDebuggingCancelledEvent -= HandleDebuggerNestedDebuggingCancelledEvent; + _runspace.IsRemoteDebuggerAttached = false; _debugger.StopDebugRunspace(_runspace); _newRunningScriptEvent.Dispose(); } diff --git a/src/System.Management.Automation/engine/EventManager.cs b/src/System.Management.Automation/engine/EventManager.cs index 75538135b92..c9cafb12782 100644 --- a/src/System.Management.Automation/engine/EventManager.cs +++ b/src/System.Management.Automation/engine/EventManager.cs @@ -1830,6 +1830,11 @@ private PSEngineEvent() { } /// public const string OnIdle = "PowerShell.OnIdle"; + /// + /// Called when Debug-Runspace has attached a debugger to the current runspace. + /// + public const string OnDebugAttach = "PowerShell.OnDebugAttach"; + /// /// Called during scriptblock invocation. /// @@ -1843,7 +1848,7 @@ private PSEngineEvent() { } /// /// A HashSet that contains all engine event names. /// - internal static readonly HashSet EngineEvents = new HashSet(StringComparer.OrdinalIgnoreCase) { Exiting, OnIdle, OnScriptBlockInvoke }; + internal static readonly HashSet EngineEvents = new HashSet(StringComparer.OrdinalIgnoreCase) { Exiting, OnIdle, OnDebugAttach, OnScriptBlockInvoke }; } /// diff --git a/src/System.Management.Automation/engine/hostifaces/Connection.cs b/src/System.Management.Automation/engine/hostifaces/Connection.cs index 3f5911d1c62..ccb918c7dd9 100644 --- a/src/System.Management.Automation/engine/hostifaces/Connection.cs +++ b/src/System.Management.Automation/engine/hostifaces/Connection.cs @@ -761,6 +761,12 @@ public string Name /// Gets the Runspace Id. /// public int Id { get; } + + /// + /// Gets and sets a boolean indicating whether the runspace has a + /// debugger attached with Debug-Runspace. + /// + public bool IsRemoteDebuggerAttached { get; internal set; } /// /// Returns protocol version that the remote server uses for PS remoting. diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Debug-Runspace.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Debug-Runspace.Tests.ps1 index 8aa28b00aef..2b517e4f301 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Debug-Runspace.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Debug-Runspace.Tests.ps1 @@ -31,6 +31,33 @@ Describe "Debug-Runspace" -Tag "CI" { $rs1.Debugger.SetDebugMode("None") { Debug-Runspace -Runspace $rs1 -ErrorAction stop } | Should -Throw -ErrorId "InvalidOperation,Microsoft.PowerShell.Commands.DebugRunspaceCommand" } + + It "Should write attach event and mark runspace as having a remote debugger attached" { + $onAttachName = [System.Management.Automation.PSEngineEvent]::OnDebugAttach + + $debugTarget = [PowerShell]::Create() + $null = $debugTarget.AddCommand('Wait-Event').AddParameter('SourceIdentifier', $onAttachName) + $waitTask = $debugTarget.BeginInvoke() + $debugTarget.Runspace.IsRemoteDebuggerAttached | Should -BeFalse + + $debugger = [PowerShell]::Create() + $null = $debugger.AddCommand('Debug-Runspace').AddParameter('Id', $debugTarget.Runspace.Id) + $debugTask = $debugger.BeginInvoke() + + $waitTask.AsyncWaitHandle.WaitOne(5000) | Should -BeTrue + $waitInfo = $debugTarget.EndInvoke($waitTask) + $waitInfo.SourceIdentifier | Should -Be $onAttachName + + $debugTarget.Runspace.IsRemoteDebuggerAttached | Should -BeTrue + + $debugger.Stop() + $exp = { + $debugger.EndInvoke($debugTask) + } | Should -Throw -PassThru + $exp.FullyQualifiedErrorId | Should -Be "PipelineStoppedException" + + $debugTarget.Runspace.IsRemoteDebuggerAttached | Should -BeFalse + } }