public static RunspaceInfo CreateFromPowerShell( ILogger logger, PowerShell pwsh, string localComputerName) { PowerShellVersionDetails psVersionDetails = PowerShellVersionDetails.GetVersionDetails(logger, pwsh); SessionDetails sessionDetails = SessionDetails.GetFromPowerShell(pwsh); bool isOnLocalMachine = string.Equals(sessionDetails.ComputerName, localComputerName, StringComparison.OrdinalIgnoreCase) || string.Equals(sessionDetails.ComputerName, "localhost", StringComparison.OrdinalIgnoreCase); RunspaceOrigin runspaceOrigin = RunspaceOrigin.Local; if (pwsh.Runspace.RunspaceIsRemote) { runspaceOrigin = pwsh.Runspace.ConnectionInfo is NamedPipeConnectionInfo ? RunspaceOrigin.EnteredProcess : RunspaceOrigin.PSSession; } return(new RunspaceInfo( pwsh.Runspace, runspaceOrigin, psVersionDetails, sessionDetails, isRemote: !isOnLocalMachine)); }
public PowerShellVersion(PowerShellVersionDetails versionDetails) { Version = versionDetails.VersionString; DisplayVersion = $"{versionDetails.Version.Major}.{versionDetails.Version.Minor}"; Edition = versionDetails.Edition; Architecture = versionDetails.Architecture switch { PowerShellProcessArchitecture.X64 => "x64", PowerShellProcessArchitecture.X86 => "x86", _ => "Architecture Unknown", }; }
public RunspaceInfo( Runspace runspace, RunspaceOrigin origin, PowerShellVersionDetails powerShellVersionDetails, SessionDetails sessionDetails, bool isRemote) { Runspace = runspace; RunspaceOrigin = origin; SessionDetails = sessionDetails; PowerShellVersionDetails = powerShellVersionDetails; IsOnRemoteMachine = isRemote; }
public static RunspaceInfo CreateFromLocalPowerShell( ILogger logger, PowerShell pwsh) { PowerShellVersionDetails psVersionDetails = PowerShellVersionDetails.GetVersionDetails(logger, pwsh); SessionDetails sessionDetails = SessionDetails.GetFromPowerShell(pwsh); return(new RunspaceInfo( pwsh.Runspace, RunspaceOrigin.Local, psVersionDetails, sessionDetails, isRemote: false)); }
public PowerShellVersion(PowerShellVersionDetails versionDetails) { this.Version = versionDetails.VersionString; this.DisplayVersion = $"{versionDetails.Version.Major}.{versionDetails.Version.Minor}"; this.Edition = versionDetails.Edition; switch (versionDetails.Architecture) { case PowerShellProcessArchitecture.X64: this.Architecture = "x64"; break; case PowerShellProcessArchitecture.X86: this.Architecture = "x86"; break; default: this.Architecture = "Architecture Unknown"; break; } }
public async Task <Unit> Handle(PsesAttachRequestArguments request, CancellationToken cancellationToken) { _debugStateService.IsAttachSession = true; _debugEventHandlerService.RegisterEventHandlers(); bool processIdIsSet = !string.IsNullOrEmpty(request.ProcessId) && request.ProcessId != "undefined"; bool customPipeNameIsSet = !string.IsNullOrEmpty(request.CustomPipeName) && request.CustomPipeName != "undefined"; PowerShellVersionDetails runspaceVersion = _powerShellContextService.CurrentRunspace.PowerShellVersion; // If there are no host processes to attach to or the user cancels selection, we get a null for the process id. // This is not an error, just a request to stop the original "attach to" request. // Testing against "undefined" is a HACK because I don't know how to make "Cancel" on quick pick loading // to cancel on the VSCode side without sending an attachRequest with processId set to "undefined". if (!processIdIsSet && !customPipeNameIsSet) { _logger.LogInformation( $"Attach request aborted, received {request.ProcessId} for processId."); throw new RpcErrorException(0, "User aborted attach to PowerShell host process."); } StringBuilder errorMessages = new StringBuilder(); if (request.ComputerName != null) { if (runspaceVersion.Version.Major < 4) { throw new RpcErrorException(0, $"Remote sessions are only available with PowerShell 4 and higher (current session is {runspaceVersion.Version})."); } else if (_powerShellContextService.CurrentRunspace.Location == RunspaceLocation.Remote) { throw new RpcErrorException(0, $"Cannot attach to a process in a remote session when already in a remote session."); } await _powerShellContextService.ExecuteScriptStringAsync( $"Enter-PSSession -ComputerName \"{request.ComputerName}\"", errorMessages).ConfigureAwait(false); if (errorMessages.Length > 0) { throw new RpcErrorException(0, $"Could not establish remote session to computer '{request.ComputerName}'"); } _debugStateService.IsRemoteAttach = true; } if (processIdIsSet && int.TryParse(request.ProcessId, out int processId) && (processId > 0)) { if (runspaceVersion.Version.Major < 5) { throw new RpcErrorException(0, $"Attaching to a process is only available with PowerShell 5 and higher (current session is {runspaceVersion.Version})."); } await _powerShellContextService.ExecuteScriptStringAsync( $"Enter-PSHostProcess -Id {processId}", errorMessages).ConfigureAwait(false); if (errorMessages.Length > 0) { throw new RpcErrorException(0, $"Could not attach to process '{processId}'"); } } else if (customPipeNameIsSet) { if (runspaceVersion.Version < s_minVersionForCustomPipeName) { throw new RpcErrorException(0, $"Attaching to a process with CustomPipeName is only available with PowerShell 6.2 and higher (current session is {runspaceVersion.Version})."); } await _powerShellContextService.ExecuteScriptStringAsync( $"Enter-PSHostProcess -CustomPipeName {request.CustomPipeName}", errorMessages).ConfigureAwait(false); if (errorMessages.Length > 0) { throw new RpcErrorException(0, $"Could not attach to process with CustomPipeName: '{request.CustomPipeName}'"); } } else if (request.ProcessId != "current") { _logger.LogError( $"Attach request failed, '{request.ProcessId}' is an invalid value for the processId."); throw new RpcErrorException(0, "A positive integer must be specified for the processId field."); } // Execute the Debug-Runspace command but don't await it because it // will block the debug adapter initialization process. The // InitializedEvent will be sent as soon as the RunspaceChanged // event gets fired with the attached runspace. string debugRunspaceCmd; if (request.RunspaceName != null) { IEnumerable <int?> ids = await _powerShellContextService.ExecuteCommandAsync <int?>(new PSCommand() .AddCommand("Microsoft.PowerShell.Utility\\Get-Runspace") .AddParameter("Name", request.RunspaceName) .AddCommand("Microsoft.PowerShell.Utility\\Select-Object") .AddParameter("ExpandProperty", "Id")); foreach (var id in ids) { _debugStateService.RunspaceId = id; break; } debugRunspaceCmd = $"\nDebug-Runspace -Name '{request.RunspaceName}'"; } else if (request.RunspaceId != null) { if (!int.TryParse(request.RunspaceId, out int runspaceId) || runspaceId <= 0) { _logger.LogError( $"Attach request failed, '{request.RunspaceId}' is an invalid value for the processId."); throw new RpcErrorException(0, "A positive integer must be specified for the RunspaceId field."); } _debugStateService.RunspaceId = runspaceId; debugRunspaceCmd = $"\nDebug-Runspace -Id {runspaceId}"; } else { _debugStateService.RunspaceId = 1; debugRunspaceCmd = "\nDebug-Runspace -Id 1"; } // Clear any existing breakpoints before proceeding await _breakpointService.RemoveAllBreakpointsAsync().ConfigureAwait(continueOnCapturedContext: false); _debugStateService.WaitingForAttach = true; Task nonAwaitedTask = _powerShellContextService .ExecuteScriptStringAsync(debugRunspaceCmd) .ContinueWith(OnExecutionCompletedAsync); if (runspaceVersion.Version.Major >= 7) { _jsonRpcServer.SendNotification(EventNames.Initialized); } return(Unit.Value); }
protected async Task HandleAttachRequestAsync( AttachRequestArguments attachParams, RequestContext <object> requestContext) { _isAttachSession = true; RegisterEventHandlers(); // If there are no host processes to attach to or the user cancels selection, we get a null for the process id. // This is not an error, just a request to stop the original "attach to" request. // Testing against "undefined" is a HACK because I don't know how to make "Cancel" on quick pick loading // to cancel on the VSCode side without sending an attachRequest with processId set to "undefined". if (string.IsNullOrEmpty(attachParams.ProcessId) || (attachParams.ProcessId == "undefined")) { Logger.Write( LogLevel.Normal, $"Attach request aborted, received {attachParams.ProcessId} for processId."); await requestContext.SendErrorAsync( "User aborted attach to PowerShell host process."); return; } StringBuilder errorMessages = new StringBuilder(); if (attachParams.ComputerName != null) { PowerShellVersionDetails runspaceVersion = _editorSession.PowerShellContext.CurrentRunspace.PowerShellVersion; if (runspaceVersion.Version.Major < 4) { await requestContext.SendErrorAsync( $"Remote sessions are only available with PowerShell 4 and higher (current session is {runspaceVersion.Version})."); return; } else if (_editorSession.PowerShellContext.CurrentRunspace.Location == RunspaceLocation.Remote) { await requestContext.SendErrorAsync( $"Cannot attach to a process in a remote session when already in a remote session."); return; } await _editorSession.PowerShellContext.ExecuteScriptStringAsync( $"Enter-PSSession -ComputerName \"{attachParams.ComputerName}\"", errorMessages); if (errorMessages.Length > 0) { await requestContext.SendErrorAsync( $"Could not establish remote session to computer '{attachParams.ComputerName}'"); return; } _isRemoteAttach = true; } if (int.TryParse(attachParams.ProcessId, out int processId) && (processId > 0)) { PowerShellVersionDetails runspaceVersion = _editorSession.PowerShellContext.CurrentRunspace.PowerShellVersion; if (runspaceVersion.Version.Major < 5) { await requestContext.SendErrorAsync( $"Attaching to a process is only available with PowerShell 5 and higher (current session is {runspaceVersion.Version})."); return; } await _editorSession.PowerShellContext.ExecuteScriptStringAsync( $"Enter-PSHostProcess -Id {processId}", errorMessages); if (errorMessages.Length > 0) { await requestContext.SendErrorAsync( $"Could not attach to process '{processId}'"); return; } // Clear any existing breakpoints before proceeding await ClearSessionBreakpointsAsync(); // Execute the Debug-Runspace command but don't await it because it // will block the debug adapter initialization process. The // InitializedEvent will be sent as soon as the RunspaceChanged // event gets fired with the attached runspace. int runspaceId = attachParams.RunspaceId > 0 ? attachParams.RunspaceId : 1; _waitingForAttach = true; Task nonAwaitedTask = _editorSession.PowerShellContext .ExecuteScriptStringAsync($"\nDebug-Runspace -Id {runspaceId}") .ContinueWith(OnExecutionCompletedAsync); } else { Logger.Write( LogLevel.Error, $"Attach request failed, '{attachParams.ProcessId}' is an invalid value for the processId."); await requestContext.SendErrorAsync( "A positive integer must be specified for the processId field."); return; } await requestContext.SendResultAsync(null); }