Как использовать стандартный поток ввода вместо Console.ReadKey - PullRequest
1 голос
/ 29 апреля 2019

Как использовать Stream.Read () вместо Console.ReadKey ()?

while (true)
{
    var c = Console.In.Read();
    Console.WriteLine("input:" + c);
}

Но значение вернется только после ввода всей строки.

Я не хочу использовать Console.ReadKey () для достижения этой функции. Потому что это плохо для тестирования.

Так как же контролировать ввод ключа пользователя через поток? Или другим способом. (Когда интерфейс не используется в максимально возможной степени)

1 Ответ

0 голосов
/ 29 апреля 2019

.NET Framework и .NET Core используют функцию Win32 ReadConsoleInput для питания Console.ReadKey(), которая одновременно считывает бит данных из буфера консоли.

Напротив, использование Console.In или Console.OpenStandardInput() вызывает функцию Win32 GetStdHandle, а затем оборачивает дескриптор в закрытый класс __ConsoleStream, который внутренне использует функции ReadFile или ReadConsole Win32, в зависимости от на несколько вещей.

По умолчанию поток консоли настроен на ENABLE_ECHO_INPUT, который отображает каждый введенный вами символ обратно на экран. Для этого необходимо установить ENABLE_LINE_INPUT.

Документация для ENABLE_LINE_INPUT гласит:

Функция ReadFile или ReadConsole возвращается только при чтении символа возврата каретки. Если этот режим отключен, функции возвращаются, когда доступны один или несколько символов.

Поэтому, если нам нужен поток, который использует ReadFile или ReadConsole, нам нужен дескриптор консоли с отключенными этими двумя флагами. Это можно сделать так:

static class Win32Console
{
    public static Stream GetConsoleStreamWithImmediateInput()
    {
        var handle = GetStdHandle(STD_INPUT_HANDLE);

        if (handle.IsInvalid) throw new Win32Exception();

        try
        {
            if (!GetConsoleMode(handle, out var mode)) throw new Win32Exception();

            mode &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);

            if (!SetConsoleMode(handle, mode)) throw new Win32Exception();
        }
        catch
        {
            handle.Close();
            throw;
        }

        return new FileStream(handle, FileAccess.Read);
    }

    const int STD_INPUT_HANDLE = -10;

    const int ENABLE_LINE_INPUT = 0x0002;
    const int ENABLE_ECHO_INPUT = 0x0004;

    [DllImport("Kernel32.dll", SetLastError = true)]
    static extern SafeFileHandle GetStdHandle(int nStdHandle);

    [DllImport("Kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool GetConsoleMode(SafeFileHandle hConsoleHandle, out int mode);

    [DllImport("Kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool SetConsoleMode(SafeFileHandle hConsoleHandle, int mode);
}

Это даст вам поток, который позволит вам читать по одному символу за раз, даже если целая строка еще не введена.

Обратите внимание, что это не будет печатать каждый символ на экране. Если вы все еще хотите этого, вам придется повторить это самостоятельно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...