如何使用标准输入流而不是 Console.ReadKey



如何使用 Stream.Read() 而不是 Console.ReadKey()?

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

但该值仅在输入整行时返回。

我不想使用 Console.ReadKey() 来实现这个功能。因为这不利于测试。

那么如何通过流监控用户的密钥输入呢?或者通过其他方式(尽可能不使用界面时)

.NETFramework和.NET Core使用Win32函数ReadConsoleInput来为Console.ReadKey()提供支持,它一次从控制台缓冲区读取一些数据。

相比之下,使用Console.InConsole.OpenStandardInput()

调用 Win32 函数GetStdHandle,然后将句柄包装在私有类__ConsoleStream中,该类在内部使用ReadFileReadConsoleWin32 函数,具体取决于一些因素。

默认情况下,控制台流配置了ENABLE_ECHO_INPUT,该 会将键入的每个字符回显到屏幕上。这需要设置ENABLE_LINE_INPUT

ENABLE_LINE_INPUT的文档指出:

ReadFileReadConsole函数仅在读取回车符时返回。如果禁用此模式,则当一个或多个字符可用时,函数将返回。

因此,如果我们想要一个使用ReadFileReadConsole,我们需要一个禁用这两个标志的控制台句柄。这可以像这样完成:

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);
}

这将为您提供一个流,让您一次读取一个字符,即使尚未输入整行。

请注意,这不会将每个字符打印到屏幕上。如果你仍然想要它,你将不得不自己回应它。

最新更新