Forum: PC-Programmierung C getch() nach GetAsyncKeyState()


von Llemaban (Gast)


Lesenswert?

Hi Zusammen

Habe ein kleines Problem bzgl. getch() nach GetAsyncKeyState().
Gleich mal mein Codeausschnitt:
1
int retKey;
2
while((GetAsyncKeyState(VK_ESCAPE)) || (GetAsyncKeyState(VK_RETURN)) || (GetAsyncKeyState(VK_LEFT)) || (GetAsyncKeyState(VK_RIGHT))); //warten bis Tasten losgelassen wurden
3
Sleep(20);
4
fflush(stdin); //bringt leider nichts!
5
while(1) //Endlosschleife hier als Beispiel
6
{
7
  retKey = _getch();
8
  printf("%c", ((char)(retKey))); //nur für Testphase
9
}

Eigentlich erwarte ich jetzt, dass ich beliebige Tasten drücken kann und 
mir diese anschliessend auf der Konsole ausgegeben werden. Macht es 
auch, aber erst nachdem der ganze Input Buffer ausgelesen wurde, in 
welchem sich noch alle meine vorangehenden Pfeiltasten, ENTER und co 
befinden! Dh meine Konsole wird erstmal zugemüllt mit '\n' und 'Ó' 
(Ó->0xE0, siehe: https://msdn.microsoft.com/en-us/library/078sfkak.aspx 
unter "Remarks").

Wie kann ich dieses Problem umgehen? fflush(stdin) bringt ja anscheinend 
nichts an dieser Stelle...

Die eingegebenen Tasten sollen später NICHT ausgegeben werden. Ich 
verwende hier den printf nur um das Problem zu veranschaulichen.

Danke im voraus...

von Heiko L. (zer0)


Lesenswert?

>_getch, _getwch
>When reading a function key or an arrow key, each function must be called twice; 
the first call returns 0 or 0xE0, and the second call returns the actual key code.

Also (sofern extended keycodes ignoriert werden sollen)
1
_getch --> 0 oder 0xE0? -- Nein --> Zeichen ausgeben
2
   ^              |
3
   |              J
4
   |              a
5
   |              |
6
   |              V
7
   ------------ _getch()

Das ist in C eine Schleife mit einem if darin.


GetAsyncKeyState() ist eine ganz andere API auf einer ganz anderen Ebene 
des Betriebssystems.

von Llemaban (Gast)


Lesenswert?

Wäre ne ganz einfache Idee, aber kann ich leider nicht nutzen :(
Problem ist, wenn man zuvor versehentlich eine andere Taste (zB einen 
Buchstaben) gedrückt hatte. Die Schleife würde alles bis zu diesem 
Zeichen perfekt entfernen, dann aber den versehentlichen Wert übernehmen 
und wenn jetzt danach noch ein ENTER kommt (und das wird so sein), wird 
ein falscher Wert abgespeichert. (mit ENTER oder ESC wird die while(1) 
quasi wieder verlassen)
Wäre super einfach deine Schleife, aber ist leider nutzlos für mein 
Problem :(

Gibt es keine andere Möglichkeit diesen Input Buffer zu leeren?

von Heiko L. (zer0)


Lesenswert?

Llemaban schrieb:
> Wäre ne ganz einfache Idee, aber kann ich leider nicht nutzen :(
Das solltest Du aber, denn sonst druckst du da Steuerzeichen in die 
Konsole...

Aber dann zum Problem:
Kann man den Buffer nicht leer lesen oder ggf. das File-Handle neu 
öffnen?

Tendenziell würde ich nicht versuchen, APIs zu mischen, die auf 
verschiedenen Ebenen liegen, da ist sowas vorprogrammiert, weil die 
Interaktion zwischen diesen Ebenen nicht ganz durchsichtig ist.

Vielleicht ist das hier für deine Zwecke besser geeignet:
https://docs.microsoft.com/en-us/windows/console/console-functions

Die Standard-Handles (stdin, stdout, stderr) sind eigentlich nicht 
unbedingt für interaktive Nutzung vorgesehen: Sie können das so 
nebenbei, allerdings mit Einschränkungen (wie Du merkst). Die sind 
interessant, wenn Pipes oder Redirection des Outputs in Dateien genutzt 
werden sollen: Da ist "Datei" eher eine passende Abstraktion.

von Llemaban (Gast)


Lesenswert?

Heiko L. schrieb:
> Vielleicht ist das hier für deine Zwecke besser geeignet:
> https://docs.microsoft.com/en-us/windows/console/console-functions

Tatsache... FlushConsoleInputBuffer() ist die Antwort, ganz oben auf der 
Liste der Cosole Functions!

Danke für den Link! Darauf kam ich bisher noch nicht.

Dh du würdest davon abraten GetAsyncKeyState() und getch() im gleichen 
Code zu verwenden?

lg Llemaban

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Llemaban schrieb:
> Dh du würdest davon abraten GetAsyncKeyState() und getch() im gleichen
> Code zu verwenden?

Ja, auf jeden Fall.

Ich verwende sowas hier:
1
static unsigned short
2
get_keypress()
3
{
4
    HANDLE         input_handle = GetStdHandle(STD_INPUT_HANDLE);
5
    DWORD          events = 0;
6
    INPUT_RECORD   input_record;
7
    DWORD          input_size = 1;
8
    unsigned short virtual_key = 0;
9
10
    int not_a_keypress = 1;
11
12
    do
13
    {
14
        FlushConsoleInputBuffer(input_handle);
15
        ReadConsoleInput(input_handle, &input_record, input_size, &events);
16
        
17
        if(input_record.EventType == KEY_EVENT && input_record.Event.KeyEvent.bKeyDown)
18
        {
19
            virtual_key = input_record.Event.KeyEvent.wVirtualKeyCode;
20
            not_a_keypress = 0;
21
        }
22
    } while(not_a_keypress);
23
24
    return virtual_key;
25
}

von georg (Gast)


Lesenswert?

Frank M. schrieb:
> Llemaban schrieb:
>> Dh du würdest davon abraten GetAsyncKeyState() und getch() im gleichen
>> Code zu verwenden?
>
> Ja, auf jeden Fall.

Ein grundlegendes Problem dürfte sein, dass die Software blockierend 
arbeietn soll, während die Key-Verarbeitung von Windows Event-Gesteuert 
ist. Da sind Funktionen wie "Warte auf eine Taste" zumindest 
unerwünscht, eigentlich sollten sie verboten sein.

Im konkreten Fall: das Konsolenprogramm wartet auf einen Tastendruck, 
der aber nur kommen kann, wenn das Konsolenfenster auch den Focus hat, 
sonst bekommt ein anderes Windowsprogramm die Taste. Eine Funktion wie 
GetAsyncKeyState() weiss aber garnicht wer den Focus hat. Ist eben das 
falsche API an dieser Stelle.

Georg

von Llemaban (Gast)


Lesenswert?

Danke für die Antworten...

Dann werde ich auf das GetAsyncKeyState verzichten und versuche es 
mittels

Frank M. schrieb:
> static unsigned short
> get_keypress()

einfach auf eine asynchrone Art.

Danke für deinen Code Frank und allen anderen auch :)

von Llemaban (Gast)


Lesenswert?

Hier meine Asynchrone Abfrage:
1
#define GetAsyncKeyInputFormat_AsciiChar    0  //Retrun Value Ascii-Character (8 Bit)
2
#define GetAsyncKeyInputFormat_UnicodeChar    1  //Return Value Unicode-Character (16 Bit)
3
#define GetAsyncKeyInputFormat_VirtualKeyCode  2  //Return Value VK_KEY (16 Bit)
4
5
int GetAsyncKeyInput(const char format)
6
{
7
  HANDLE input_handle = GetStdHandle(STD_INPUT_HANDLE);
8
  DWORD input_n;
9
  int retVal = -1;
10
  
11
  GetNumberOfConsoleInputEvents(input_handle, &input_n);
12
  if(input_n > 0)
13
  {
14
    INPUT_RECORD input_record;
15
    DWORD input_size = 1, events = 0;
16
    ReadConsoleInput(input_handle, &input_record, input_size, &events);
17
    if((input_record.EventType == KEY_EVENT) && (input_record.Event.KeyEvent.bKeyDown))
18
    {
19
      switch(format)
20
      {
21
      case GetAsyncKeyInputFormat_AsciiChar:
22
        retVal = (int)input_record.Event.KeyEvent.uChar.AsciiChar;
23
        break;
24
      case GetAsyncKeyInputFormat_UnicodeChar:
25
        retVal = (int)input_record.Event.KeyEvent.uChar.UnicodeChar;
26
        break;
27
      case GetAsyncKeyInputFormat_VirtualKeyCode:
28
        retVal = (int)input_record.Event.KeyEvent.wVirtualKeyCode;
29
        break;
30
      }
31
    }
32
  }
33
34
  return retVal;
35
}

Für "format" entsprechend einer der definierten Formate verwenden, 
dementsprechend wird der Rückgabewert erstellt.
Ein Rückgabewert von -1 entsteht, wenn der console input buffer leer 
ist.

lg Llemaban

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.