Esempio n. 1
0
        public async Task <List <PSCommandMessage> > Handle(GetCommandParams request, CancellationToken cancellationToken)
        {
            PSCommand psCommand = new PSCommand();

            // Executes the following:
            // Get-Command -CommandType Function,Cmdlet,ExternalScript | Select-Object -Property Name,ModuleName | Sort-Object -Property Name
            psCommand
            .AddCommand("Microsoft.PowerShell.Core\\Get-Command")
            .AddParameter("CommandType", new[] { "Function", "Cmdlet", "ExternalScript" })
            .AddCommand("Microsoft.PowerShell.Utility\\Select-Object")
            .AddParameter("Property", new[] { "Name", "ModuleName" })
            .AddCommand("Microsoft.PowerShell.Utility\\Sort-Object")
            .AddParameter("Property", "Name");

            IEnumerable <PSObject> result = await _powerShellContextService.ExecuteCommandAsync <PSObject>(psCommand);

            var commandList = new List <PSCommandMessage>();

            if (result != null)
            {
                foreach (dynamic command in result)
                {
                    commandList.Add(new PSCommandMessage
                    {
                        Name                = command.Name,
                        ModuleName          = command.ModuleName,
                        Parameters          = command.Parameters,
                        ParameterSets       = command.ParameterSets,
                        DefaultParameterSet = command.DefaultParameterSet
                    });
                }
            }

            return(commandList);
        }
Esempio n. 2
0
        /// <summary>
        /// Gets the CommandInfo instance for a command with a particular name.
        /// </summary>
        /// <param name="commandName">The name of the command.</param>
        /// <param name="powerShellContext">The PowerShellContext to use for running Get-Command.</param>
        /// <returns>A CommandInfo object with details about the specified command.</returns>
        public static async Task <CommandInfo> GetCommandInfoAsync(
            string commandName,
            PowerShellContextService powerShellContext)
        {
            Validate.IsNotNull(nameof(commandName), commandName);
            Validate.IsNotNull(nameof(powerShellContext), powerShellContext);

            // Make sure the command's noun isn't blacklisted.  This is
            // currently necessary to make sure that Get-Command doesn't
            // load PackageManagement or PowerShellGet because they cause
            // a major slowdown in IntelliSense.
            var commandParts = commandName.Split('-');

            if (commandParts.Length == 2 && NounExclusionList.ContainsKey(commandParts[1]))
            {
                return(null);
            }

            PSCommand command = new PSCommand();

            command.AddCommand(@"Microsoft.PowerShell.Core\Get-Command");
            command.AddArgument(commandName);
            command.AddParameter("ErrorAction", "Ignore");

            return((await powerShellContext.ExecuteCommandAsync <PSObject>(command, sendOutputToHost: false, sendErrorToHost: false).ConfigureAwait(false))
                   .Select(o => o.BaseObject)
                   .OfType <CommandInfo>()
                   .FirstOrDefault());
        }
        public async Task <List <PSCommandMessage> > Handle(GetCommandParams request, CancellationToken cancellationToken)
        {
            PSCommand psCommand = new PSCommand();

            // Executes the following:
            // Get-Command -CommandType Function,Cmdlet,ExternalScript | Sort-Object -Property Name
            psCommand
            .AddCommand("Microsoft.PowerShell.Core\\Get-Command")
            .AddParameter("CommandType", new[] { "Function", "Cmdlet", "ExternalScript" })
            .AddCommand("Microsoft.PowerShell.Utility\\Sort-Object")
            .AddParameter("Property", "Name");

            IEnumerable <CommandInfo> result = await _powerShellContextService.ExecuteCommandAsync <CommandInfo>(psCommand).ConfigureAwait(false);

            var commandList = new List <PSCommandMessage>();

            if (result != null)
            {
                foreach (CommandInfo command in result)
                {
                    // Some info objects have a quicker way to get the DefaultParameterSet. These
                    // are also the most likely to show up so win-win.
                    string defaultParameterSet = null;
                    switch (command)
                    {
                    case CmdletInfo info:
                        defaultParameterSet = info.DefaultParameterSet;
                        break;

                    case FunctionInfo info:
                        defaultParameterSet = info.DefaultParameterSet;
                        break;
                    }

                    if (defaultParameterSet == null)
                    {
                        // Try to get the default ParameterSet if it isn't streamlined (ExternalScriptInfo for example)
                        foreach (CommandParameterSetInfo parameterSetInfo in command.ParameterSets)
                        {
                            if (parameterSetInfo.IsDefault)
                            {
                                defaultParameterSet = parameterSetInfo.Name;
                                break;
                            }
                        }
                    }

                    commandList.Add(new PSCommandMessage
                    {
                        Name                = command.Name,
                        ModuleName          = command.ModuleName,
                        Parameters          = command.Parameters,
                        ParameterSets       = command.ParameterSets,
                        DefaultParameterSet = defaultParameterSet
                    });
                }
            }

            return(commandList);
        }
        public async Task <string> InvokeReadLineAsync(bool isCommandLine, CancellationToken cancellationToken)
        {
            _readLineCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
            var localTokenSource = _readLineCancellationSource;

            if (localTokenSource.Token.IsCancellationRequested)
            {
                throw new TaskCanceledException();
            }

            if (!isCommandLine)
            {
                return(await _consoleReadLine.InvokeLegacyReadLineAsync(
                           isCommandLine : false,
                           _readLineCancellationSource.Token).ConfigureAwait(false));
            }

            var readLineCommand = new PSCommand()
                                  .AddCommand(s_lazyInvokeReadLineForEditorServicesCmdletInfo.Value)
                                  .AddParameter("CancellationToken", _readLineCancellationSource.Token);

            IEnumerable <string> readLineResults = await _powerShellContext.ExecuteCommandAsync <string>(
                readLineCommand,
                errorMessages : null,
                s_psrlExecutionOptions).ConfigureAwait(false);

            string line = readLineResults.FirstOrDefault();

            return(cancellationToken.IsCancellationRequested
                ? string.Empty
                : line);
        }
        public async Task <List <Breakpoint> > GetBreakpointsAsync()
        {
            if (BreakpointApiUtils.SupportsBreakpointApis)
            {
                return(BreakpointApiUtils.GetBreakpoints(
                           _powerShellContextService.CurrentRunspace.Runspace.Debugger,
                           _debugStateService.RunspaceId));
            }

            // Legacy behavior
            PSCommand psCommand = new PSCommand();

            psCommand.AddCommand(@"Microsoft.PowerShell.Utility\Get-PSBreakpoint");
            IEnumerable <Breakpoint> breakpoints = await _powerShellContextService.ExecuteCommandAsync <Breakpoint>(psCommand);

            return(breakpoints.ToList());
        }
        public async Task ExecuteAsync()
        {
            var results =
                await _powerShellContext.ExecuteCommandAsync <TResult>(
                    _psCommand,
                    _errorMessages,
                    _executionOptions);

            var unusedTask = Task.Run(() => _resultsTask.SetResult(results));
        }
Esempio n. 7
0
        public async Task ExecuteAsync()
        {
            var results =
                await _powerShellContext.ExecuteCommandAsync <TResult>(
                    _psCommand,
                    _errorMessages,
                    _executionOptions).ConfigureAwait(false);

            _ = Task.Run(() => _resultsTask.SetResult(results));
        }
Esempio n. 8
0
        /// <summary>
        /// Gets the command's "Synopsis" documentation section.
        /// </summary>
        /// <param name="commandInfo">The CommandInfo instance for the command.</param>
        /// <param name="powerShellContext">The PowerShellContext to use for getting command documentation.</param>
        /// <returns></returns>
        public static async Task <string> GetCommandSynopsisAsync(
            CommandInfo commandInfo,
            PowerShellContextService powerShellContext)
        {
            Validate.IsNotNull(nameof(commandInfo), commandInfo);
            Validate.IsNotNull(nameof(powerShellContext), powerShellContext);

            // A small optimization to not run Get-Help on things like DSC resources.
            if (commandInfo.CommandType != CommandTypes.Cmdlet &&
                commandInfo.CommandType != CommandTypes.Function &&
                commandInfo.CommandType != CommandTypes.Filter)
            {
                return(string.Empty);
            }

            // If we have a synopsis cached, return that.
            // NOTE: If the user runs Update-Help, it's possible that this synopsis will be out of date.
            // Given the perf increase of doing this, and the simple workaround of restarting the extension,
            // this seems worth it.
            if (s_synopsisCache.TryGetValue(commandInfo.Name, out string synopsis))
            {
                return(synopsis);
            }

            PSCommand command = new PSCommand()
                                .AddCommand(@"Microsoft.PowerShell.Core\Get-Help")
                                // We use .Name here instead of just passing in commandInfo because
                                // CommandInfo.ToString() duplicates the Prefix if one exists.
                                .AddParameter("Name", commandInfo.Name)
                                .AddParameter("Online", false)
                                .AddParameter("ErrorAction", "Ignore");

            var results = await powerShellContext.ExecuteCommandAsync <PSObject>(command, sendOutputToHost : false, sendErrorToHost : false).ConfigureAwait(false);

            PSObject helpObject = results.FirstOrDefault();

            // Extract the synopsis string from the object
            string synopsisString =
                (string)helpObject?.Properties["synopsis"].Value ??
                string.Empty;

            // Only cache cmdlet infos because since they're exposed in binaries, the can never change throughout the session.
            if (commandInfo.CommandType == CommandTypes.Cmdlet)
            {
                s_synopsisCache.TryAdd(commandInfo.Name, synopsisString);
            }

            // Ignore the placeholder value for this field
            if (string.Equals(synopsisString, "SHORT DESCRIPTION", System.StringComparison.CurrentCultureIgnoreCase))
            {
                return(string.Empty);
            }

            return(synopsisString);
        }
Esempio n. 9
0
        public async Task <Unit> Handle(ShowHelpParams request, CancellationToken cancellationToken)
        {
            const string CheckHelpScript = @"
                [CmdletBinding()]
                param (
                    [String]$CommandName
                )
                try {
                    $command = Microsoft.PowerShell.Core\Get-Command $CommandName -ErrorAction Stop
                } catch [System.Management.Automation.CommandNotFoundException] {
                    $PSCmdlet.ThrowTerminatingError($PSItem)
                }
                try {
                    $helpUri = [Microsoft.PowerShell.Commands.GetHelpCodeMethods]::GetHelpUri($command)

                    $oldSslVersion = [System.Net.ServicePointManager]::SecurityProtocol
                    [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12

                    # HEAD means we don't need the content itself back, just the response header
                    $status = (Microsoft.PowerShell.Utility\Invoke-WebRequest -Method Head -Uri $helpUri -TimeoutSec 5 -ErrorAction Stop).StatusCode
                    if ($status -lt 400) {
                        $null = Microsoft.PowerShell.Core\Get-Help $CommandName -Online
                        return
                    }
                } catch {
                    # Ignore - we want to drop out to Get-Help -Full
                } finally {
                    [System.Net.ServicePointManager]::SecurityProtocol = $oldSslVersion
                }

                return Microsoft.PowerShell.Core\Get-Help $CommandName -Full
                ";

            string helpParams = request.Text;

            if (string.IsNullOrEmpty(helpParams))
            {
                helpParams = "Get-Help";
            }

            PSCommand checkHelpPSCommand = new PSCommand()
                                           .AddScript(CheckHelpScript, useLocalScope: true)
                                           .AddArgument(helpParams);

            // TODO: Rather than print the help in the console, we should send the string back
            //       to VSCode to display in a help pop-up (or similar)
            await _powerShellContextService.ExecuteCommandAsync <PSObject>(
                checkHelpPSCommand, sendOutputToHost : true, cancellationToken : cancellationToken).ConfigureAwait(false);

            return(Unit.Value);
        }
Esempio n. 10
0
        public async Task <RunspaceResponse[]> Handle(GetRunspaceParams request, CancellationToken cancellationToken)
        {
            IEnumerable <PSObject> runspaces = null;

            if (request.ProcessId == null)
            {
                request.ProcessId = "current";
            }

            // If the processId is a valid int, we need to run Get-Runspace within that process
            // otherwise just use the current runspace.
            if (int.TryParse(request.ProcessId, out int pid))
            {
                // Create a remote runspace that we will invoke Get-Runspace in.
                using (var rs = RunspaceFactory.CreateRunspace(new NamedPipeConnectionInfo(pid)))
                    using (var ps = PowerShell.Create())
                    {
                        rs.Open();
                        ps.Runspace = rs;
                        // Returns deserialized Runspaces. For simpler code, we use PSObject and rely on dynamic later.
                        runspaces = ps.AddCommand("Microsoft.PowerShell.Utility\\Get-Runspace").Invoke <PSObject>();
                    }
            }
            else
            {
                var psCommand = new PSCommand().AddCommand("Microsoft.PowerShell.Utility\\Get-Runspace");
                var sb        = new StringBuilder();
                // returns (not deserialized) Runspaces. For simpler code, we use PSObject and rely on dynamic later.
                runspaces = await _powerShellContextService.ExecuteCommandAsync <PSObject>(
                    psCommand, sb, cancellationToken : cancellationToken).ConfigureAwait(false);
            }

            var runspaceResponses = new List <RunspaceResponse>();

            if (runspaces != null)
            {
                foreach (dynamic runspace in runspaces)
                {
                    runspaceResponses.Add(
                        new RunspaceResponse
                    {
                        Id           = runspace.Id,
                        Name         = runspace.Name,
                        Availability = runspace.RunspaceAvailability.ToString()
                    });
                }
            }

            return(runspaceResponses.ToArray());
        }
Esempio n. 11
0
        /// <summary>
        /// Gets the CommandInfo instance for a command with a particular name.
        /// </summary>
        /// <param name="commandName">The name of the command.</param>
        /// <param name="powerShellContext">The PowerShellContext to use for running Get-Command.</param>
        /// <returns>A CommandInfo object with details about the specified command.</returns>
        public static async Task <CommandInfo> GetCommandInfoAsync(
            string commandName,
            PowerShellContextService powerShellContext)
        {
            Validate.IsNotNull(nameof(commandName), commandName);
            Validate.IsNotNull(nameof(powerShellContext), powerShellContext);

            // If we have a CommandInfo cached, return that.
            if (s_commandInfoCache.TryGetValue(commandName, out CommandInfo cmdInfo))
            {
                return(cmdInfo);
            }

            // Make sure the command's noun or command's name isn't in the exclusion lists.
            // This is currently necessary to make sure that Get-Command doesn't
            // load PackageManagement or PowerShellGet v2 because they cause
            // a major slowdown in IntelliSense.
            var commandParts = commandName.Split('-');

            if ((commandParts.Length == 2 && s_nounExclusionList.Contains(commandParts[1])) ||
                s_cmdletExclusionList.Contains(commandName))
            {
                return(null);
            }

            PSCommand command = new PSCommand();

            command.AddCommand(@"Microsoft.PowerShell.Core\Get-Command");
            command.AddArgument(commandName);
            command.AddParameter("ErrorAction", "Ignore");

            CommandInfo commandInfo = (await powerShellContext.ExecuteCommandAsync <PSObject>(command, sendOutputToHost: false, sendErrorToHost: false).ConfigureAwait(false))
                                      .Select(o => o.BaseObject)
                                      .OfType <CommandInfo>()
                                      .FirstOrDefault();

            // Only cache CmdletInfos since they're exposed in binaries they are likely to not change throughout the session.
            if (commandInfo?.CommandType == CommandTypes.Cmdlet)
            {
                s_commandInfoCache.TryAdd(commandName, commandInfo);
            }

            return(commandInfo);
        }
Esempio n. 12
0
        public async Task <string> InvokeReadLineAsync(bool isCommandLine, CancellationToken cancellationToken)
        {
            _readLineCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
            var localTokenSource = _readLineCancellationSource;

            if (localTokenSource.Token.IsCancellationRequested)
            {
                throw new TaskCanceledException();
            }

            try
            {
                if (!isCommandLine)
                {
                    return(await _consoleReadLine.InvokeLegacyReadLineAsync(
                               false,
                               _readLineCancellationSource.Token));
                }

                var result = (await _powerShellContext.ExecuteCommandAsync <string>(
                                  new PSCommand()
                                  .AddScript(ReadLineScript)
                                  .AddArgument(_readLineCancellationSource.Token),
                                  null,
                                  new ExecutionOptions()
                {
                    WriteErrorsToHost = false,
                    WriteOutputToHost = false,
                    InterruptCommandPrompt = false,
                    AddToHistory = false,
                    IsReadLine = isCommandLine
                }))
                             .FirstOrDefault();

                return(cancellationToken.IsCancellationRequested
                    ? string.Empty
                    : result);
            }
            finally
            {
                _readLineCancellationSource = null;
            }
        }
        public async Task <ExpandAliasResult> Handle(ExpandAliasParams request, CancellationToken cancellationToken)
        {
            const string script = @"
function __Expand-Alias {

    param($targetScript)

    [ref]$errors=$null

    $tokens = [System.Management.Automation.PsParser]::Tokenize($targetScript, $errors).Where({$_.type -eq 'command'}) |
                    Sort-Object Start -Descending

    foreach ($token in  $tokens) {
        $definition=(Get-Command ('`'+$token.Content) -CommandType Alias -ErrorAction SilentlyContinue).Definition

        if($definition) {
            $lhs=$targetScript.Substring(0, $token.Start)
            $rhs=$targetScript.Substring($token.Start + $token.Length)

            $targetScript=$lhs + $definition + $rhs
       }
    }

    $targetScript
}";

            // TODO: Refactor to not rerun the function definition every time.
            var psCommand = new PSCommand();

            psCommand
            .AddScript(script)
            .AddStatement()
            .AddCommand("__Expand-Alias")
            .AddArgument(request.Text);
            var result = await _powerShellContextService.ExecuteCommandAsync <string>(
                psCommand, cancellationToken : cancellationToken).ConfigureAwait(false);

            return(new ExpandAliasResult
            {
                Text = result.First()
            });
        }
Esempio n. 14
0
        /// <summary>
        /// Gets the command's "Synopsis" documentation section.
        /// </summary>
        /// <param name="commandInfo">The CommandInfo instance for the command.</param>
        /// <param name="powerShellContext">The PowerShellContext to use for getting command documentation.</param>
        /// <returns></returns>
        public static async Task <string> GetCommandSynopsisAsync(
            CommandInfo commandInfo,
            PowerShellContextService powerShellContext)
        {
            Validate.IsNotNull(nameof(commandInfo), commandInfo);
            Validate.IsNotNull(nameof(powerShellContext), powerShellContext);

            // A small optimization to not run Get-Help on things like DSC resources.
            if (commandInfo.CommandType != CommandTypes.Cmdlet &&
                commandInfo.CommandType != CommandTypes.Function &&
                commandInfo.CommandType != CommandTypes.Filter)
            {
                return(string.Empty);
            }

            PSCommand command = new PSCommand()
                                .AddCommand(@"Microsoft.PowerShell.Core\Get-Help")
                                // We use .Name here instead of just passing in commandInfo because
                                // CommandInfo.ToString() duplicates the Prefix if one exists.
                                .AddParameter("Name", commandInfo.Name)
                                .AddParameter("ErrorAction", "Ignore");

            var results = await powerShellContext.ExecuteCommandAsync <PSObject>(command, sendOutputToHost : false, sendErrorToHost : false).ConfigureAwait(false);

            PSObject helpObject = results.FirstOrDefault();

            // Extract the synopsis string from the object
            string synopsisString =
                (string)helpObject?.Properties["synopsis"].Value ??
                string.Empty;

            // Ignore the placeholder value for this field
            if (string.Equals(synopsisString, "SHORT DESCRIPTION", System.StringComparison.CurrentCultureIgnoreCase))
            {
                return(string.Empty);
            }

            return(synopsisString);
        }
        /// <summary>
        /// Gets the command's "Synopsis" documentation section.
        /// </summary>
        /// <param name="commandInfo">The CommandInfo instance for the command.</param>
        /// <param name="powerShellContext">The PowerShellContext to use for getting command documentation.</param>
        /// <returns></returns>
        public static async Task <string> GetCommandSynopsisAsync(
            CommandInfo commandInfo,
            PowerShellContextService powerShellContext)
        {
            Validate.IsNotNull(nameof(powerShellContext), powerShellContext);

            string synopsisString = string.Empty;

            if (commandInfo != null &&
                (commandInfo.CommandType == CommandTypes.Cmdlet ||
                 commandInfo.CommandType == CommandTypes.Function ||
                 commandInfo.CommandType == CommandTypes.Filter))
            {
                PSCommand command = new PSCommand();
                command.AddCommand(@"Microsoft.PowerShell.Core\Get-Help");
                command.AddArgument(commandInfo);
                command.AddParameter("ErrorAction", "Ignore");

                var results = await powerShellContext.ExecuteCommandAsync <PSObject>(command, sendOutputToHost : false, sendErrorToHost : false).ConfigureAwait(false);

                PSObject helpObject = results.FirstOrDefault();

                if (helpObject != null)
                {
                    // Extract the synopsis string from the object
                    synopsisString =
                        (string)helpObject.Properties["synopsis"].Value ??
                        string.Empty;

                    // Ignore the placeholder value for this field
                    if (string.Equals(synopsisString, "SHORT DESCRIPTION", System.StringComparison.CurrentCultureIgnoreCase))
                    {
                        synopsisString = string.Empty;
                    }
                }
            }

            return(synopsisString);
        }
        private async Task CheckPackageManagement()
        {
            PSCommand getModule = new PSCommand().AddCommand("Get-Module").AddParameter("ListAvailable").AddParameter("Name", "PackageManagement");

            foreach (PSModuleInfo module in await _powerShellContextService.ExecuteCommandAsync <PSModuleInfo>(getModule).ConfigureAwait(false))
            {
                // The user has a good enough version of PackageManagement
                if (module.Version >= s_desiredPackageManagementVersion)
                {
                    break;
                }

                _logger.LogDebug("Old version of PackageManagement detected.");

                if (_powerShellContextService.CurrentRunspace.Runspace.SessionStateProxy.LanguageMode != PSLanguageMode.FullLanguage)
                {
                    _languageServer.Window.ShowWarning("You have an older version of PackageManagement known to cause issues with the PowerShell extension. Please run the following command in a new Windows PowerShell session and then restart the PowerShell extension: `Install-Module PackageManagement -Force -AllowClobber -MinimumVersion 1.4.6`");
                    return;
                }

                var takeActionText = "Yes";
                MessageActionItem messageAction = await _languageServer.Window.ShowMessageRequest(new ShowMessageRequestParams
                {
                    Message = "You have an older version of PackageManagement known to cause issues with the PowerShell extension. Would you like to update PackageManagement (You will need to restart the PowerShell extension after)?",
                    Type    = MessageType.Warning,
                    Actions = new[]
                    {
                        new MessageActionItem
                        {
                            Title = takeActionText
                        },
                        new MessageActionItem
                        {
                            Title = "Not now"
                        }
                    }
                }).ConfigureAwait(false);

                // If the user chose "Not now" ignore it for the rest of the session.
                if (messageAction?.Title == takeActionText)
                {
                    StringBuilder errors = new StringBuilder();
                    await _powerShellContextService.ExecuteScriptStringAsync(
                        "powershell.exe -NoLogo -NoProfile -Command '[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Install-Module -Name PackageManagement -Force -MinimumVersion 1.4.6 -Scope CurrentUser -AllowClobber -Repository PSGallery'",
                        errors,
                        writeInputToHost : true,
                        writeOutputToHost : true,
                        addToHistory : true).ConfigureAwait(false);

                    if (errors.Length == 0)
                    {
                        _logger.LogDebug("PackageManagement is updated.");
                        _languageServer.Window.ShowMessage(new ShowMessageParams
                        {
                            Type    = MessageType.Info,
                            Message = "PackageManagement updated, If you already had PackageManagement loaded in your session, please restart the PowerShell extension."
                        });
                    }
                    else
                    {
                        // There were errors installing PackageManagement.
                        _logger.LogError($"PackageManagement installation had errors: {errors.ToString()}");
                        _languageServer.Window.ShowMessage(new ShowMessageParams
                        {
                            Type    = MessageType.Error,
                            Message = "PackageManagement update failed. This might be due to PowerShell Gallery using TLS 1.2. More info can be found at https://aka.ms/psgallerytls"
                        });
                    }
                }
            }
        }
Esempio n. 17
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);
        }
        private async Task CheckPackageManagement()
        {
            PSCommand getModule = new PSCommand().AddCommand("Get-Module").AddParameter("ListAvailable").AddParameter("Name", "PackageManagement");

            foreach (PSModuleInfo module in await _powerShellContextService.ExecuteCommandAsync <PSModuleInfo>(getModule))
            {
                // The user has a good enough version of PackageManagement
                if (module.Version >= s_desiredPackageManagementVersion)
                {
                    break;
                }

                _logger.LogDebug("Old version of PackageManagement detected. Attempting to update.");

                var takeActionText = "Yes";
                MessageActionItem messageAction = await _languageServer.Window.ShowMessage(new ShowMessageRequestParams
                {
                    Message = "You have an older version of PackageManagement known to cause issues with the PowerShell extension. Would you like to update PackageManagement (You will need to restart the PowerShell extension after)?",
                    Type    = MessageType.Warning,
                    Actions = new []
                    {
                        new MessageActionItem
                        {
                            Title = takeActionText
                        },
                        new MessageActionItem
                        {
                            Title = "Not now"
                        }
                    }
                });

                // If the user chose "Not now" ignore it for the rest of the session.
                if (messageAction?.Title == takeActionText)
                {
                    StringBuilder errors = new StringBuilder();
                    await _powerShellContextService.ExecuteScriptStringAsync(
                        "powershell.exe -NoLogo -NoProfile -Command 'Install-Module -Name PackageManagement -Force -MinimumVersion 1.4.6 -Scope CurrentUser -AllowClobber'",
                        errors,
                        writeInputToHost : true,
                        writeOutputToHost : true,
                        addToHistory : true).ConfigureAwait(false);

                    if (errors.Length == 0)
                    {
                        _languageServer.Window.ShowMessage(new ShowMessageParams
                        {
                            Type    = MessageType.Info,
                            Message = "PackageManagement updated, If you already had PackageManagement loaded in your session, please restart the PowerShell extension."
                        });
                    }
                    else
                    {
                        // There were errors installing PackageManagement.
                        _languageServer.Window.ShowMessage(new ShowMessageParams
                        {
                            Type    = MessageType.Error,
                            Message = "PackageManagement update failed. Please run the following command in a new Windows PowerShell session and then restart the PowerShell extension: `Install-Module PackageManagement -Force -AllowClobber -MinimumVersion 1.4.6`"
                        });
                    }
                }
            }
        }
Esempio n. 19
0
        /// <summary>
        /// Start the debug server listening.
        /// </summary>
        /// <returns>A task that completes when the server is ready.</returns>
        public async Task StartAsync()
        {
            _jsonRpcServer = await JsonRpcServer.From(options =>
            {
                options.Serializer    = new DapProtocolSerializer();
                options.Receiver      = new DapReceiver();
                options.LoggerFactory = _loggerFactory;
                ILogger logger        = options.LoggerFactory.CreateLogger("DebugOptionsStartup");

                // We need to let the PowerShell Context Service know that we are in a debug session
                // so that it doesn't send the powerShell/startDebugger message.
                _powerShellContextService = ServiceProvider.GetService <PowerShellContextService>();
                _powerShellContextService.IsDebugServerActive = true;

                // Needed to make sure PSReadLine's static properties are initialized in the pipeline thread.
                // This is only needed for Temp sessions who only have a debug server.
                if (_usePSReadLine && _useTempSession && Interlocked.Exchange(ref s_hasRunPsrlStaticCtor, 1) == 0)
                {
                    var command = new PSCommand()
                                  .AddCommand(s_lazyInvokeReadLineConstructorCmdletInfo.Value);

                    // This must be run synchronously to ensure debugging works
                    _powerShellContextService
                    .ExecuteCommandAsync <object>(command, sendOutputToHost: true, sendErrorToHost: true)
                    .GetAwaiter()
                    .GetResult();
                }

                options.Services = new ServiceCollection()
                                   .AddPsesDebugServices(ServiceProvider, this, _useTempSession);

                options
                .WithInput(_inputStream)
                .WithOutput(_outputStream);

                logger.LogInformation("Adding handlers");

                options
                .WithHandler <InitializeHandler>()
                .WithHandler <LaunchHandler>()
                .WithHandler <AttachHandler>()
                .WithHandler <DisconnectHandler>()
                .WithHandler <SetFunctionBreakpointsHandler>()
                .WithHandler <SetExceptionBreakpointsHandler>()
                .WithHandler <ConfigurationDoneHandler>()
                .WithHandler <ThreadsHandler>()
                .WithHandler <SetBreakpointsHandler>()
                .WithHandler <StackTraceHandler>()
                .WithHandler <ScopesHandler>()
                .WithHandler <VariablesHandler>()
                .WithHandler <ContinueHandler>()
                .WithHandler <NextHandler>()
                .WithHandler <PauseHandler>()
                .WithHandler <StepInHandler>()
                .WithHandler <StepOutHandler>()
                .WithHandler <SourceHandler>()
                .WithHandler <SetVariableHandler>()
                .WithHandler <DebugEvaluateHandler>();

                logger.LogInformation("Handlers added");
            }).ConfigureAwait(false);
        }
        /// <summary>
        /// Start the debug server listening.
        /// </summary>
        /// <returns>A task that completes when the server is ready.</returns>
        public async Task StartAsync()
        {
            _debugAdapterServer = await DebugAdapterServer.From(options =>
            {
                // We need to let the PowerShell Context Service know that we are in a debug session
                // so that it doesn't send the powerShell/startDebugger message.
                _powerShellContextService = ServiceProvider.GetService <PowerShellContextService>();
                _powerShellContextService.IsDebugServerActive = true;

                // Needed to make sure PSReadLine's static properties are initialized in the pipeline thread.
                // This is only needed for Temp sessions who only have a debug server.
                if (_usePSReadLine && _useTempSession && Interlocked.Exchange(ref s_hasRunPsrlStaticCtor, 1) == 0)
                {
                    var command = new PSCommand()
                                  .AddCommand(s_lazyInvokeReadLineConstructorCmdletInfo.Value);

                    // This must be run synchronously to ensure debugging works
                    _powerShellContextService
                    .ExecuteCommandAsync <object>(command, sendOutputToHost: true, sendErrorToHost: true)
                    .GetAwaiter()
                    .GetResult();
                }

                options
                .WithInput(_inputStream)
                .WithOutput(_outputStream)
                .WithServices(serviceCollection => serviceCollection
                              .AddLogging()
                              .AddOptions()
                              .AddPsesDebugServices(ServiceProvider, this, _useTempSession))
                // TODO: Consider replacing all WithHandler with AddSingleton
                .WithHandler <LaunchAndAttachHandler>()
                .WithHandler <DisconnectHandler>()
                .WithHandler <BreakpointHandlers>()
                .WithHandler <ConfigurationDoneHandler>()
                .WithHandler <ThreadsHandler>()
                .WithHandler <StackTraceHandler>()
                .WithHandler <ScopesHandler>()
                .WithHandler <VariablesHandler>()
                .WithHandler <DebuggerActionHandlers>()
                .WithHandler <SourceHandler>()
                .WithHandler <SetVariableHandler>()
                .WithHandler <DebugEvaluateHandler>()
                // The OnInitialize delegate gets run when we first receive the _Initialize_ request:
                // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Initialize
                .OnInitialize(async(server, request, cancellationToken) => {
                    var breakpointService = server.GetService <BreakpointService>();
                    // Clear any existing breakpoints before proceeding
                    await breakpointService.RemoveAllBreakpointsAsync().ConfigureAwait(false);
                })
                // The OnInitialized delegate gets run right before the server responds to the _Initialize_ request:
                // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Initialize
                .OnInitialized((server, request, response, cancellationToken) => {
                    response.SupportsConditionalBreakpoints    = true;
                    response.SupportsConfigurationDoneRequest  = true;
                    response.SupportsFunctionBreakpoints       = true;
                    response.SupportsHitConditionalBreakpoints = true;
                    response.SupportsLogPoints   = true;
                    response.SupportsSetVariable = true;

                    return(Task.CompletedTask);
                });
            }).ConfigureAwait(false);
        }