Problem mit KeyHook



  • @5cript Ich weiss nicht was das alles mit Keyboard-Hooks zu tun haben soll, die machen hier überhaupt keinen Sinn. Erst recht globale. Dass der OP danach fragt is OK, ihm fehlt vermutlich einfach die Erfahrung. Aber deinen Link ... verstehe ich nicht ganz.

    @CUser1 Wenn du ein Spiel machst, hast du normalerweise einen Game-Loop der pro Frame 1x durchlaufen wird. Die einfachste Lösung wäre mMn. dort das bereits von @Martin-Richter erwähnte GetAsyncKeyState zu verwenden um alle relevanten Tasten abzufragen.

    Davon abgesehen lies dir bitte zumindest den Punkt "Frage nach dem, was du erreichen willst und nicht danach womit du es erreichen willst" hier durch: https://www.c-plusplus.net/forum/topic/200753/du-brauchst-hilfe

    Das wissen wir nämlich immer noch nicht. Du hast zwar das Thema Spiele erwähnt, aber so wie du die Frage formuliert hast ("Aber wie machen z. B. Computerspiele das?") klingt das für micht nicht so als ob du ein Computerspiel schreiben wolltest.



  • @CUser1 sagte in Problem mit KeyHook:

    Dann müsste man die WndProc Hooken weil in SetWindowsHook erhalte ich kein Key_up und Key_Down oder? Was ist das mit KF_DOWN?

    Wieso hooken? Die Window-Proc kannst du selbst angeben, wieso solltest du die dann hooken wollen?

    Setzen die dann bei key_down einen Wert wie "Spieler ist auf der Taste" auf wahr und in der Mainloop "wenn wahr, dann ... "?

    Kann man so machen. Oder einfach pollen (GetAsyncKeyState).



  • @hustbaer sagte in Problem mit KeyHook:

    @5cript Ich weiss nicht was das alles mit Keyboard-Hooks zu tun haben soll, die machen hier überhaupt keinen Sinn. Erst recht globale. Dass der OP danach fragt is OK, ihm fehlt vermutlich einfach die Erfahrung. Aber deinen Link ... verstehe ich nicht ganz.

    Wir wissen nicht, was er warum macht, und er hat mit hooks angefangen, dann bleib ich auch bei hooks.
    Wenn sein Problem hooks erfordert, dann wechsel ich nicht auf was anderes, womit er gar nichts anfangen kann.
    Wir wissen nicht ob sein Programm fokus hat.



  • So hier sind jetzt sehr viele Antworten, die ich der Reihe nach lesen werde.

    Aber soviel: Es geht mir darum durch eine injected Dll in einem ANDEREN Prozess eine Keyboardabfrage zu machen.

    Und da habe ich einen Hook in der Mainloop und das Problem ist, dass wenn man im Spiel auf der Taste "bleibt" dann feuert der Charakter konstant sehr schnell und sendet entsprechende Packets dafür an den Server.

    Dieses Timing erreiche ich nicht mit "selber einzeln drücken" und Abfrage durch SetWindowHook Umleitung, überhaupt weil das dann eben erst verzögert anfängt und ich bis dahin vom Gegner schon besiegt wurde.

    Das Spiel selber macht es ja auch irgendwie, also muss es gehen. Weil wenn man im Chat schreibt dann läuft das über die WndProc aber wenn man auf der Maustaste bleibt dann feuert es eben SOFORT und KONSTANT Spells ab.

    Ich hab es jetzt so gelöst dass ich in der Mainloop eine GetKeyState Abfrage mache die pausenlos abfragt ob ich z. B. "VK_SPACE" gedrückt habe und wenn ja, dann führt es bestimmte Funktionen aus. Jedoch kann ich dort keine Sleeps verwenden, weil ich eben in der Mainloop von dem Spiel bin. Da bräuchte ich wieder GetTickCount. Und ich weiß nicht ob das so überhaupt professionell gelöst ist.

    Wo speichert eigentlich Windows die ganzen "Messages" ab bis sie dann zu gegebenem Zeitpunkt an die Prozesse versendet werden?



  • @hustbaer Das heißt es führt gar nichts über das GetKeyState pollen hinweg? Weil ich immer lese man soll nicht pollen?



  • @CUser1 sagte in Problem mit KeyHook:

    Ich hab es jetzt so gelöst dass ich in der Mainloop eine GetKeyState Abfrage mache die pausenlos abfragt ob ich z. B. "VK_SPACE" gedrückt habe und wenn ja, dann führt es bestimmte Funktionen aus.

    Klingt vernünftig.

    Jedoch kann ich dort keine Sleeps verwenden, weil ich eben in der Mainloop von dem Spiel bin. Da bräuchte ich wieder GetTickCount.

    Ja, du brauchst dann irgend eine Clock. Und GetTickCount ist die billigste Variante (=wenig Overhead) für Windows die ich kenne.

    Und ich weiß nicht ob das so überhaupt professionell gelöst ist.

    Ich würde sagen für diesen Anwendungsfall ist das völlig OK. Würde ich vermutlich auch so machen.

    Das heißt es führt gar nichts über das GetKeyState pollen hinweg?

    Irgendwas wirst du immer pollen müssen wenn die Aktion im Game-Loop ausgeführt werden soll -- und wenn es nur ein bool-Flag ist.

    Weil ich immer lese man soll nicht pollen?

    Für die meisten Fälle stimmt das ja auch. Angenommen du programmierst einen Text-Editor, der einfach nichts tun soll so lange der User keinen Input liefert. Da wäre es sehr schlecht immer wieder alle Tasten zu pollen. Weil du damit unnötig Rechenzeit verheizt.

    Ein Spiel, das sowieso immer den Game-Loop ausführt und neue Frames rendert, ist eine etwas andere Situation. So lange das Spiel läuft verheizt das sowieso massiv Rechenzeit, da kommt es dann auf das Polling von ein paar Tasten auch nicht mehr an.

    Wenn du den Overhead der durch das Polling entsteht verringern möchtest, kannst du natürlich auf Messages reagieren. Du kannst dann z.B. im "key down" Message Handler wenn die Leertaste gedrückt wird eine bool Variable auf true setzen, und wenn sie losgelassen wird setzt du sie wieder auf false. Dann musst du im Game-Loop nur noch diese Variable prüfen, was verhältnismässig viel billiger ist als GetAsyncKeyState aufzurufen.

    Andrerseits sind ein paar GetAsyncKeyState pro Frame auch überhaupt kein Problem weil die Aufrufe absolut gesehen schnell genug sind. Du wirst den Unterschied zu der Message-Handler + bool Variante vermutlich nicht merken. Von daher würde ich persönlich die einfachere Variante wählen = GetAsyncKeyState.



  • @hustbaer Ich muss doch AsyncKeyState verwenden, weil sonst drücke ich kurz auf VK_SPACE und wenn das Timing nicht stimmt ist die Mainloop grade wo anders in der Ausführung.

    Wie funktioniert das überhaupt mit den Messages? Ich hab dieses Fenster offen. Dann drücke ich einen Buchstaben. Was passiert dann der Reihe nach? Woher weiß Windows, dass die Taste gedrückt wurde? Wie wird daraus eine Message. Und wenn jetzt ein Window GetMessage aufruft, woher weiß es dann dass die Message für mich bestimmt ist? Bekommt immer das Foregroundwindow alle Messages?



  • Ich muss doch AsyncKeyState verwenden, weil sonst drücke ich kurz auf VK_SPACE und wenn das Timing nicht stimmt ist die Mainloop grade wo anders in der Ausführung.

    Verstehe nicht was du meinst. Genau das Problem hast du doch mit GetAsyncKeyState. Wobei das ein Problem ist das oft einfach ignoriert wird. So kurze Tastendrücke dass die Taste innerhalb eines Frames gedrückt und wieder losgelassen wird kann man üblicherweise ignorieren.

    Wie funktioniert das überhaupt mit den Messages? Ich hab dieses Fenster offen. Dann drücke ich einen Buchstaben. Was passiert dann der Reihe nach? Woher weiß Windows, dass die Taste gedrückt wurde? Wie wird daraus eine Message. Und wenn jetzt ein Window GetMessage aufruft, woher weiß es dann dass die Message für mich bestimmt ist? Bekommt immer das Foregroundwindow alle Messages?

    Das vollständig zu beschreiben würde etwas den Rahmen sprengen. Stark vereinfacht läuft das so:

    Dass eine Taste gedrückt wurde bekommt Windows von einem Treiber mitgeteilt, z.B. einem USB-Tastaturtreiber.

    Dann schaut Windows welches Fenster den Input-Fokus hat. Das ist meistens, aber muss nicht unbedingt immer das Foreground-Window sein.

    Und dann wird eine Message in die Message-Queue für dieses Fenster eingefügt. (Jedes Fenster ist einem Thread zugeordnet, und jeder Thread kann eine Message-Queue haben. Wenn ein Thread noch keine Message-Queue hat, und es wird eine gebraucht, dann wird sie in dem Moment automatisch angelegt.)

    Falls der Thread dann gerade GetMessage ausführt und auf eine neue Message wartet (weil die Queue leer ist), dann wird GetMessage "aufgeweckt" und kommt mit der neuen Message zurück.

    Ansonsten verbleibt die Message in der Queue und wird dann irgendwann wenn GetMessage später aufgerufen wird zurückgegeben. (Nicht unbedingt als erste Message, denn es kann ja sein dass bereits andere Messages in der Queue waren - die würden dann davor zurückgegeben werden.)

    Dann ruft der Thread normalerweise DispatchMessage auf. DispatchMessage guckt sich dann an um welches Fenster es geht (das Window Handle ist ja Teil der MSG Struktur), holt sich die Window-Procedure für dieses Fenster und ruft sie auf.



  • @hustbaer Achso es funktioniert eh mit GetAsyncKeyState auch nicht, wenn ich nur ganz kurz auf der Taste bin. Das ist nervig, dann führt doch nichts über die Key_Down und bool Variante herum weil ich sonst immer auf der Taste sein muss, wenn gerade getKeyState aufgerufen wird,

    Läuft der Treiber ganze Zeit im Hintergrund wie ein normaler Prozess und wartet auf Eingaben oder wie? Und in Windows ist irgendein Prozess dann wiederrum irgendwann damit beschäftigt die aufzuarbeiten? Ist der explorer.exe für das alles zuständig?

    Mir ist außerdem aufgefallen, dass GetAsyncKeyState auch anschlägt wenn ein anderes Window aktiv ist. Das heißt ein Buchstabe wird an alle Messagequeues von allen Threads gesendet.



  • @CUser1 sagte in Problem mit KeyHook:

    @hustbaer Achso es funktioniert eh mit GetAsyncKeyState auch nicht, wenn ich nur ganz kurz auf der Taste bin.

    Korrekt.
    Bzw. genau genommen funktioniert es schon: Bit 0 des Returnwertes ist 1 wenn die Taste irgendwann zwischen dem letzten Aufruf von GetAsyncKeyState gedrückt wurde -- auch wenn sie dazwischen wieder losgelassen wurde. Blöd ist nur wenn auch noch jemand anderes GetAsyncKeyState verwendet. Dann funktioniert der Spass nicht mehr.

    Das steht aber auch alles in der Doku: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getasynckeystate

    Das ist nervig, dann führt doch nichts über die Key_Down und bool Variante herum weil ich sonst immer auf der Taste sein muss, wenn gerade getKeyState aufgerufen wird,

    Naja, doch, ist eigentlich ganz einfach: ignoriere es. Machen viele Spiele und es ist kaum jemals ein Problem.

    Läuft der Treiber ganze Zeit im Hintergrund wie ein normaler Prozess und wartet auf Eingaben oder wie?

    Das kommt auf den Treiber drauf an und was du genau unter "läuft im Hintergrund" meinst. Der Treiber ist auf jeden Fall immer geladen (existiert im Speicher). Aber bei den meisten Geräten ist es nicht erforderlich permanent irgendwas zu pollen oder so. Üblicherweise löst ein Gerät einen Interrupt aus wenn es etwas braucht bzw. zu melden hat. Durch so einen Interrupt wird dann irgendein gerade laufender Thread unterbrochen, in den Kernel-Mode gewechselt und der Interrupt-Handler ausgeführt. Dort plaudert der Treiber dann mit dem Gerät und leitet z.B. die Info ans Betriebssystem weiter dass jetzt eine Taste gedrückt wurde. Weil der Treiber nett ist beeilt er sich dabei, und gibt dann Kontrolle wieder an den unterbrochenen Thread zurück.

    Und in Windows ist irgendein Prozess dann wiederrum irgendwann damit beschäftigt die aufzuarbeiten?

    Jain. Es gibt natürlich den sogenannten "System" Prozess. Der bearbeitet aber nicht alles was Treiber so machen. Einige Dinge werden vom Treiber auch mehr oder weniger direkt an einen bestimmten Prozess geschickt. Natürlich ist dabei auch das Betriebssystem im Spiel, aber nicht notwendigerweise der "System" Prozess.

    Ist der explorer.exe für das alles zuständig?

    Nein. Windows läuft wunderbar ohne dass explorer.exe läuft oder auch nur auf der HDD/SSD vorhanden ist.

    Mir ist außerdem aufgefallen, dass GetAsyncKeyState auch anschlägt wenn ein anderes Window aktiv ist. Das heißt ein Buchstabe wird an alle Messagequeues von allen Threads gesendet.

    Ja, GetAsyncKeyState geht auch wenn ein anderes Fenster aktiv ist. Nein, das heisst nicht dass ein Buchstabe an alle Messagequeues von allen Threads gesendet wird. Es heisst dass GetAsyncKeyState nicht auf irgendwelche Messages angewiesen ist. Windows selbst hat quasi einen Puffer wo es sich für jede Taste den aktuellen Zustand merkt. GetAsyncKeyState fragt diesen Zustand dann ab.

    D.h. ein Tastendruck wird vom Treiber erstmal an Windows geschickt. Windows merkt sich dann den aktuellen Zustand der Taste, und erzeugt dann die nötige Input-Message für den Thread der gerade den Input-Fokus hat.

    Wenn du die Maus bewegst funktioniert das ähnlich. Jedes Programm kann jederzeit wenn es möchte die Mausposition abfragen. Das heisst aber nicht dass bei jeder Mausbewegung sämtliche Programme bzw. Fenster benachrichtigt würden.


Log in to reply