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));
        }
示例#5
0
        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;
            }
        }
示例#6
0
        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);
        }