mikrocontroller.net

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


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Llemaban (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Zusammen

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

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...

Autor: Heiko L. (zer0)
Datum:

Bewertung
0 lesenswert
nicht 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)
_getch --> 0 oder 0xE0? -- Nein --> Zeichen ausgeben
   ^              |
   |              J
   |              a
   |              |
   |              V
   ------------ _getch()

Das ist in C eine Schleife mit einem if darin.


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

Autor: Llemaban (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Heiko L. (zer0)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Llemaban (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Frank M. (ukw) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
static unsigned short
get_keypress()
{
    HANDLE         input_handle = GetStdHandle(STD_INPUT_HANDLE);
    DWORD          events = 0;
    INPUT_RECORD   input_record;
    DWORD          input_size = 1;
    unsigned short virtual_key = 0;

    int not_a_keypress = 1;

    do
    {
        FlushConsoleInputBuffer(input_handle);
        ReadConsoleInput(input_handle, &input_record, input_size, &events);
        
        if(input_record.EventType == KEY_EVENT && input_record.Event.KeyEvent.bKeyDown)
        {
            virtual_key = input_record.Event.KeyEvent.wVirtualKeyCode;
            not_a_keypress = 0;
        }
    } while(not_a_keypress);

    return virtual_key;
}

Autor: georg (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Llemaban (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 :)

Autor: Llemaban (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier meine Asynchrone Abfrage:
#define GetAsyncKeyInputFormat_AsciiChar    0  //Retrun Value Ascii-Character (8 Bit)
#define GetAsyncKeyInputFormat_UnicodeChar    1  //Return Value Unicode-Character (16 Bit)
#define GetAsyncKeyInputFormat_VirtualKeyCode  2  //Return Value VK_KEY (16 Bit)

int GetAsyncKeyInput(const char format)
{
  HANDLE input_handle = GetStdHandle(STD_INPUT_HANDLE);
  DWORD input_n;
  int retVal = -1;
  
  GetNumberOfConsoleInputEvents(input_handle, &input_n);
  if(input_n > 0)
  {
    INPUT_RECORD input_record;
    DWORD input_size = 1, events = 0;
    ReadConsoleInput(input_handle, &input_record, input_size, &events);
    if((input_record.EventType == KEY_EVENT) && (input_record.Event.KeyEvent.bKeyDown))
    {
      switch(format)
      {
      case GetAsyncKeyInputFormat_AsciiChar:
        retVal = (int)input_record.Event.KeyEvent.uChar.AsciiChar;
        break;
      case GetAsyncKeyInputFormat_UnicodeChar:
        retVal = (int)input_record.Event.KeyEvent.uChar.UnicodeChar;
        break;
      case GetAsyncKeyInputFormat_VirtualKeyCode:
        retVal = (int)input_record.Event.KeyEvent.wVirtualKeyCode;
        break;
      }
    }
  }

  return retVal;
}

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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.