示例#1
0
        // Reads a line in _readLineSB when consumeKeys is true,
        //              or _availableKeys when consumeKeys is false.
        // Returns whether the line was terminated using the Enter key.
        private bool ReadLineCore(bool consumeKeys)
        {
            Debug.Assert(_tmpKeys.Count == 0);
            Debug.Assert(consumeKeys || _availableKeys.Count == 0);

            // _availableKeys either contains a line that was already read,
            // or we need to read a new line from stdin.
            bool freshKeys = _availableKeys.Count == 0;

            // Don't carry over chars from previous ReadLine call.
            _readLineSB.Clear();

            Interop.Sys.InitializeConsoleBeforeRead();
            try
            {
                // Read key-by-key until we've read a line.
                while (true)
                {
                    ConsoleKeyInfo keyInfo = freshKeys ? ReadKey() : _availableKeys.Pop();
                    if (!consumeKeys && keyInfo.Key != ConsoleKey.Backspace) // backspace is the only character not written out in the below if/elses.
                    {
                        _tmpKeys.Push(keyInfo);
                    }

                    // Handle the next key.  Since for other functions we may have ended up reading some of the user's
                    // input, we need to be able to handle manually processing that input, and so we do that processing
                    // for all input.  As such, we need to special-case a few characters, e.g. recognizing when Enter is
                    // pressed to end a line.  We also need to handle Backspace specially, to fix up both our buffer of
                    // characters and the position of the cursor.  More advanced processing would be possible, but we
                    // try to keep this very simple, at least for now.
                    if (keyInfo.Key == ConsoleKey.Enter)
                    {
                        if (freshKeys)
                        {
                            Console.WriteLine();
                        }
                        return(true);
                    }
                    else if (IsEol(keyInfo.KeyChar))
                    {
                        return(false);
                    }
                    else if (keyInfo.Key == ConsoleKey.Backspace)
                    {
                        bool removed = false;
                        if (consumeKeys)
                        {
                            int len = _readLineSB.Length;
                            if (len > 0)
                            {
                                _readLineSB.Length = len - 1;
                                removed            = true;
                            }
                        }
                        else
                        {
                            removed = _tmpKeys.TryPop(out _);
                        }

                        if (removed && freshKeys)
                        {
                            // The ReadLine input may wrap across terminal rows and we need to handle that.
                            // note: ConsolePal will cache the cursor position to avoid making many slow cursor position fetch operations.
                            if (ConsolePal.TryGetCursorPosition(out int left, out int top, reinitializeForRead: true) &&
                                left == 0 && top > 0)
                            {
                                if (s_clearToEol == null)
                                {
                                    s_clearToEol = ConsolePal.TerminalFormatStrings.Instance.ClrEol ?? string.Empty;
                                }

                                // Move to end of previous line
                                ConsolePal.SetCursorPosition(ConsolePal.WindowWidth - 1, top - 1);
                                // Clear from cursor to end of the line
                                ConsolePal.WriteStdoutAnsiString(s_clearToEol, mayChangeCursorPosition: false);
                            }
                            else
                            {
                                if (s_moveLeftString == null)
                                {
                                    string?moveLeft = ConsolePal.TerminalFormatStrings.Instance.CursorLeft;
                                    s_moveLeftString = !string.IsNullOrEmpty(moveLeft) ? moveLeft + " " + moveLeft : string.Empty;
                                }

                                Console.Write(s_moveLeftString);
                            }
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.Tab)
                    {
                        if (consumeKeys)
                        {
                            _readLineSB.Append(keyInfo.KeyChar);
                        }
                        if (freshKeys)
                        {
                            Console.Write(' ');
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.Clear)
                    {
                        _readLineSB.Clear();
                        if (freshKeys)
                        {
                            Console.Clear();
                        }
                    }
                    else if (keyInfo.KeyChar != '\0')
                    {
                        if (consumeKeys)
                        {
                            _readLineSB.Append(keyInfo.KeyChar);
                        }
                        if (freshKeys)
                        {
                            Console.Write(keyInfo.KeyChar);
                        }
                    }
                }
示例#2
0
        private string?ReadLine(bool consumeKeys)
        {
            Debug.Assert(_tmpKeys.Count == 0);
            string?readLineStr = null;

            Interop.Sys.InitializeConsoleBeforeRead();
            try
            {
                // Read key-by-key until we've read a line.
                while (true)
                {
                    // Read the next key.  This may come from previously read keys, from previously read but
                    // unprocessed data, or from an actual stdin read.
                    bool           previouslyProcessed;
                    ConsoleKeyInfo keyInfo = ReadKey(out previouslyProcessed);
                    if (!consumeKeys && keyInfo.Key != ConsoleKey.Backspace) // backspace is the only character not written out in the below if/elses.
                    {
                        _tmpKeys.Push(keyInfo);
                    }

                    // Handle the next key.  Since for other functions we may have ended up reading some of the user's
                    // input, we need to be able to handle manually processing that input, and so we do that processing
                    // for all input.  As such, we need to special-case a few characters, e.g. recognizing when Enter is
                    // pressed to end a line.  We also need to handle Backspace specially, to fix up both our buffer of
                    // characters and the position of the cursor.  More advanced processing would be possible, but we
                    // try to keep this very simple, at least for now.
                    if (keyInfo.Key == ConsoleKey.Enter)
                    {
                        readLineStr = _readLineSB.ToString();
                        _readLineSB.Clear();
                        if (!previouslyProcessed)
                        {
                            Console.WriteLine();
                        }
                        break;
                    }
                    else if (IsEol(keyInfo.KeyChar))
                    {
                        string line = _readLineSB.ToString();
                        _readLineSB.Clear();
                        if (line.Length > 0)
                        {
                            readLineStr = line;
                        }
                        break;
                    }
                    else if (keyInfo.Key == ConsoleKey.Backspace)
                    {
                        int len = _readLineSB.Length;
                        if (len > 0)
                        {
                            _readLineSB.Length = len - 1;
                            if (!previouslyProcessed)
                            {
                                // The ReadLine input may wrap accross terminal rows and we need to handle that.
                                // note: ConsolePal will cache the cursor position to avoid making many slow cursor position fetch operations.
                                if (ConsolePal.TryGetCursorPosition(out int left, out int top, reinitializeForRead: true) &&
                                    left == 0 && top > 0)
                                {
                                    if (s_clearToEol == null)
                                    {
                                        s_clearToEol = ConsolePal.TerminalFormatStrings.Instance.ClrEol ?? string.Empty;
                                    }

                                    // Move to end of previous line
                                    ConsolePal.SetCursorPosition(ConsolePal.WindowWidth - 1, top - 1);
                                    // Clear from cursor to end of the line
                                    ConsolePal.WriteStdoutAnsiString(s_clearToEol, mayChangeCursorPosition: false);
                                }
                                else
                                {
                                    if (s_moveLeftString == null)
                                    {
                                        string?moveLeft = ConsolePal.TerminalFormatStrings.Instance.CursorLeft;
                                        s_moveLeftString = !string.IsNullOrEmpty(moveLeft) ? moveLeft + " " + moveLeft : string.Empty;
                                    }

                                    Console.Write(s_moveLeftString);
                                }
                            }
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.Tab)
                    {
                        _readLineSB.Append(keyInfo.KeyChar);
                        if (!previouslyProcessed)
                        {
                            Console.Write(' ');
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.Clear)
                    {
                        _readLineSB.Clear();
                        if (!previouslyProcessed)
                        {
                            Console.Clear();
                        }
                    }
                    else if (keyInfo.KeyChar != '\0')
                    {
                        _readLineSB.Append(keyInfo.KeyChar);
                        if (!previouslyProcessed)
                        {
                            Console.Write(keyInfo.KeyChar);
                        }
                    }
                }
示例#3
0
        public unsafe IChildProcessStateHolder SpawnProcess(
            ref ChildProcessStartInfoInternal startInfo,
            string resolvedPath,
            SafeHandle stdIn,
            SafeHandle stdOut,
            SafeHandle stdErr)
        {
            var arguments            = startInfo.Arguments;
            var environmentVariables = startInfo.EnvironmentVariables;
            var workingDirectory     = startInfo.WorkingDirectory;
            var flags = startInfo.Flags;

            Debug.Assert(startInfo.CreateNewConsole || ConsolePal.HasConsoleWindow());

            var commandLine      = WindowsCommandLineUtil.MakeCommandLine(resolvedPath, arguments ?? Array.Empty <string>(), !flags.HasDisableArgumentQuoting());
            var environmentBlock = startInfo.UseCustomEnvironmentVariables ? WindowsEnvironmentBlockUtil.MakeEnvironmentBlock(environmentVariables.Span) : null;

            // Objects that need cleanup
            InputWriterOnlyPseudoConsole?pseudoConsole   = null;
            SafeJobObjectHandle?         jobObjectHandle = null;
            SafeProcessHandle?           processHandle   = null;
            SafeThreadHandle?            threadHandle    = null;

            try
            {
                pseudoConsole = startInfo.CreateNewConsole ? InputWriterOnlyPseudoConsole.Create() : null;
                if (pseudoConsole is not null && flags.HasUseCustomCodePage())
                {
                    ChangeCodePage(pseudoConsole, startInfo.CodePage, workingDirectory);
                }

                bool killOnClose = startInfo.AllowSignal && WindowsVersion.NeedsWorkaroundForWindows1809;
                jobObjectHandle = CreateJobObject(killOnClose, startInfo.DisableWindowsErrorReportingDialog);

                using var inheritableHandleStore = new InheritableHandleStore(3);
                var childStdIn = stdIn != null?inheritableHandleStore.Add(stdIn) : null;

                var childStdOut = stdOut != null?inheritableHandleStore.Add(stdOut) : null;

                var childStdErr = stdErr != null?inheritableHandleStore.Add(stdErr) : null;

                IntPtr jobObjectHandles = jobObjectHandle.DangerousGetHandle();

                Span <IntPtr> inheritableHandles = stackalloc IntPtr[inheritableHandleStore.Count];
                inheritableHandleStore.DangerousGetHandles(inheritableHandles);
                fixed(IntPtr *pInheritableHandles = inheritableHandles)
                {
                    using var attr = new ProcThreadAttributeList(3);
                    if (pseudoConsole is not null)
                    {
                        attr.UpdatePseudoConsole(pseudoConsole.Handle.DangerousGetHandle());
                    }
                    attr.UpdateHandleList(pInheritableHandles, inheritableHandles.Length);
                    attr.UpdateJobList(&jobObjectHandles, 1);

                    const int CreationFlags =
                        Kernel32.CREATE_UNICODE_ENVIRONMENT
                        | Kernel32.EXTENDED_STARTUPINFO_PRESENT;

                    int processId;

                    (processId, processHandle, threadHandle) = InvokeCreateProcess(
                        commandLine,
                        CreationFlags,
                        environmentBlock,
                        workingDirectory,
                        childStdIn,
                        childStdOut,
                        childStdErr,
                        attr);

                    return(new WindowsChildProcessState(processId, processHandle, jobObjectHandle, pseudoConsole, startInfo.AllowSignal));
                }
            }
            catch
            {
                if (processHandle is not null)
                {
                    Kernel32.TerminateProcess(processHandle, -1);
                    processHandle.Dispose();
                }
                pseudoConsole?.Dispose();
                jobObjectHandle?.Dispose();
                throw;
            }
            finally
            {
                threadHandle?.Dispose();
            }
        }