Hello! I’m new to C # and I’m interested in the question: how do I make the console recognize only single keystrokes and ignore their clamping? (And is it possible to do this at all in console C #? UPD: Added code to understand. The task is for the console to display the number only after a single keystroke, and when clamped, to output nothing.

static void Main(string[] args) { Console.WriteLine("Press \u0022Enter\u0022 to play!"); int num = 0; void WaitForKey(ConsoleKey key) { while (Console.ReadKey(true).Key != key) { } } for (int i = 1; i <= 10; i++) { WaitForKey(ConsoleKey.Enter); Console.Write("{0}", num); num++; } } 

UPD.2: a new version of the code, now the problem is that between pressing one key you have to press another.

  static void Main(string[] args) { Console.WriteLine("Press \u0022Enter\u0022 to play!"); int num = 0; for (int i = 1; i <= 10; i++) { do { ConsoleKeyInfo keypress; ConsoleKeyInfo oldkeypress; keypress = Console.ReadKey(true); oldkeypress = keypress; keypress = Console.ReadKey(true); if (oldkeypress != keypress) { Console.WriteLine("{0}", num); num++; ; oldkeypress = keypress; } } while (1==1); } 
  • A little confusing question. Do you want that when you press the key in the console, let's say "A", then what will not happen? And if "AS" it should react? - Pavel
  • If you need to read one character, then Console.ReadKey() will help with this, keystrokes will be ignored. And if you still need to keep the word / line, then only write the checks yourself. - Nikolay
  • @Pavel, I want, when I press (relatively speaking) A. I would use A only 1 time instead of AAAAAAAAAA. - YRSHKHN
  • 2
    I think, without using WinAPI there is no solution here - Andrey NOP
  • @Andrey and WinAPI is not much easier, but it is possible - rdorn

2 answers 2

Your task is more difficult than it seems. Problems:

  1. Regular console programs do not listen to system input events and, as a result, cannot process them without additional distortions.

  2. The methods of reading the System.Console class only read the input stream (not necessarily the standard one), but do not control it, so you cannot distinguish pressing a key repeatedly with releasing it from a simple “clamping” or “sticking” key, since the system records all keystrokes and folds into the input stream.

  3. From the preceding paragraphs, it follows that even with the involvement of WinAPI through PInvoke, without work and, in a sense, crutches are indispensable.

Not scared? Then let's go.

I intend to leave behind the scenes options with the connection of the message manager, hidden windows and other not very honest, with respect to the console methods. Those interested can write this in a separate answer. There is only hardcore, only honest and clean console + a little WinAPI (PInvoke)! =)

We will need to import two WinAPI functions: getkeystate ( WinAPI , PInvoke ) and getkeyboardstate ( WinAPI , PInvoke ). Let's do it in a separate class.

 using System; using System.ComponentModel; using System.Linq; using System.Runtime.InteropServices; static class ConsoleNative { //получение состояния всех клавиш, включая служебные и мышь [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetKeyboardState(byte [] lpKeyState); //сюда будем писать состояния клавиш private static byte[] keys = new byte[256]; //получение состояния указанной клавиши [System.Runtime.InteropServices.DllImport("user32.dll")] private static extern short GetKeyState(int key); 

The import of the necessary is completed, we make a check for "click". For example, we will process all virtual keys, including the mouse, you can easily change this behavior by adding an exception to unnecessary keys (the list of keys is here )

  public static bool IsAnyKeyPressed() { //По совету с PInvoke, добавлено для того, чтобы нормально отрабатывала //GetKeyboardState GetKeyState(0); if(!GetKeyboardState(keys)) { //защита на всякий случай, у меня ни разу не сработала, но... int err = Marshal.GetLastWin32Error(); throw new Win32Exception(err); } //каждый байт - это состояние одной из клавиш for(int i = 0; i < 256; i++) { if(i < 7) { //байты с 0 по 6 отвечают за клавиши мыши, проигнорируем их keys[i] = 0; } else { //за состояние "нажатости" отвечает старший бит, остальное просто зануляем keys[i] = (byte)(keys[i] & 0x80); } } return keys.Any(k => k != 0); } 

Add a couple of helpers for beauty and convenience when using

  public static void WaitForKeyDown() { bool isAnyKeyPressed; do { isAnyKeyPressed = IsAnyKeyPressed(); } while(!isAnyKeyPressed); } public static void WaitForKeyUp() { bool isAnyKeyPressed; do { isAnyKeyPressed = IsAnyKeyPressed(); } while(isAnyKeyPressed); } //очистка входного потока от нажатых клавиш. Мы же не хотим //при следующем вызове Console.ReadXXX прочитать все, что нажимали до этого? public static void FlushInputStream() { while(Console.KeyAvailable) Console.ReadKey(true); } } 

Everything, the class for working with the keyboard is completed, but you can add something of your own there, if necessary.

Now how to use this monster? We look, it is relatively easy, although of course there are some conventions:

 static void Main(string[] args) { do { //ждем, пока нажмут клавишу ConsoleNative.WaitForKeyDown(); //ждем пока отпустят клавишу ConsoleNative.WaitForKeyUp(); //с помощью Console.ReadKey читаем что было нажато var key = Console.ReadKey(true); if(!Console.KeyAvailable) { //если было одиночное нажатие выводим нажатое Console.WriteLine(key.KeyChar); } else { //если было залипание чистим входной поток от мусора ConsoleNative.FlushInputStream(); } } while(true); } 

The trick here is that the wait for pressing and releasing the keys is not made by reading the input stream, but by direct requests to the system. This allows later to read from the stream only what interests us and dump the excess.

In the described example, there is one drawback, if the keys are pressed quickly enough, some keys will not be processed, we have not yet managed to find a beautiful solution, but the task in general is already being solved - the program responds only to single presses.

Individual fragments can be improved or written more elegantly, but this is just a demonstration example of how this problem can be solved without resorting to clever tricks with hidden windows, etc.

  • To go nuts. Thanks for the detailed answer, I will definitely try. And where are the keys to zero, instead of 0x80 write each key from that list except for what I need? - YRSHKHN
  • @SergeyYershykhin for those that need keys [i] = keys [i] & 0x80, for those that do not need keys [i] = 0 - rdorn
  • That is, keys [i] = 0 & (key number)? - YRSHKHN
  • @SergeyYershykhin key code is determined by the index in the array, so to ignore the key you just need to skip the element with the index that matches the key code or enter in the element 0 - rdorn
  • stupid Can you please show how to ignore it on the example of any key? - YRSHKHN

Corrected the code now he is supposed to come up with the idea.

 using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp2 { class Program { static void Main(string[] args) { ConsoleKeyInfo keypress; ConsoleKeyInfo oldkeypress; keypress = Console.ReadKey(true); oldkeypress = keypress; do { keypress = Console.ReadKey(true); // read keystrokes if (oldkeypress != keypress) { Console.Write(keypress.KeyChar); oldkeypress = keypress; } } while (1==1); } } } 
  • Redirect output to nowhere, so as not to output anything to the console at all and just put the processing on ctrl + e and alt + e? No, it is not even close to what the author asked. He asked about the fact that if you hold down the key and do not release it, there will be a permanent conclusion and you should avoid it. - Alex Krass
  • @AlexKrass exactly! - YRSHKHN
  • @ArchiFox no, it's not that. Alex Krass more correctly described my problem. - YRSHKHN
  • @SergeyYershykhin well now try to find a solution. - ArchiFox
  • @SergeyYershykhin corrected the code look .. perhaps this implementation is suitable .. if there is no then sorry, there are no more ideas. - ArchiFox