![]() |
Tastaturhook -> Tasten manupulieren
Hallo,
vielleicht sollte ich euch erst einmal meine Problemstellung beschreiben: Da ich ja in Wettberwerben in Tastschreiben (= Tastaturschreiben) teilnehme, bin ich natürlich sehr interessiert in Möglichkeiten wie ich schneller werde. Ein paar gewürfelte Menschen haben sich schon ausgedacht, in Word die Autokorrektur-Funktion zu benutzen, damit sie häufige Wörter abkürzen können. Ein paar Beispiele: der -> r die -> i das -> s werden -> wn unseren -> usn Es sieht zwar irgenwie aus als würde es nicht viel bringen, doch wenn man Texte genauer ansieht, wird man feststellen, dass viele solche Wörter vorhanden sind und man so in der Minute 50-100 Anschläge mehr bekommen kann. Nun nochmal zurück, natürlich gibt man sich als Programmierer nicht mit dieser simplen Word-Funktion zufrieden. :) Und es hat natürlich noch einen anderen Grund: Ich kann die Abkürzungen nur in Word benutzen, will ich aber beim Chatten oder bei Fragen in die Delphi-Praxis schreiben muss ich wieder alles ausschreiben. :) Ich dachte jetzt daran, dass ich vielleicht einen Tataturhook dafür benutzen sollte, aber wie manipulier ich dann die Tasten. Das blöde ist halt z. B. bei "werden" was ja mit "wn" abgekürzt wird, da müsste ich erst wieder das "n" löschen... Könnt ihr mir helfen? |
Liste der Anhänge anzeigen (Anzahl: 1)
Ich hoffe das hilft dir, wenn nicht sag bescheid!
|
Erst mal Danke für den Quellcode!
Leider hat er aber einen kleinen Bug: Er ersetzt auch, wenn ich nur "n" eintippe... :-/ Aber die Idee an sich ist nicht schlecht! Ich glaub ich kann den jetzt auch selber ausbauen! Thx |
@dadu:
Gibts vielleicht irgendeine Systemmessage auf die man reagieren kann, um die Tastendrücke zu lesen? Mit nem Timer is das ziemlich ungenau, weil man ja nich in gleichen Intervallen Tasten drückt... |
Naja, ich denke mal man kommt nicht drum rum für jede Taste einen Hook zu schreiben, oder?
Ich selber hab mein kleines Projekt eingestellt, weil ich jetzt eh mehr oder weniger komplett auf Linux umgestiegen bin... :) |
Diesen Code verwendet dadu um rauszufinden, ob und wenn ja welche Taste (in diesem Code erstmal nur Buchstaben) gedrückt wurde:
Delphi-Quellcode:
Das Ganze hat er in einen Timer gesetzt, damit man alles mitbekommt.
//65 - 96 = Chars A- '
for i := 65 to 96 do //Wenn eine Taste gedrückt wird if (GetasyncKeyState(word(i)) <> 0) then str := str + char(i); Gibts halt nur das Problem, dass man wenn man den Timer z.B. auf 10ms stellt, das Programm dann glaubt eine Taste (die man halt nur etwas länger gedrückt hat, aber trotzdem nur einen Buchstaben geschrieben hat) wäre häufiger als 1 Mal gedrückt worden. Wenn man den Timer dann aber zu langsam einstellt (z.B. 100ms), werden manche Tasten nicht empfangen weil sie außerhalb des Intervalls gedrückt werden. Da man ja nicht immer in selben Abständen beim Tippen Tasten betätigt, ist es mit einem Timer nicht sicher möglich, jeden Tastendruck zu erwischen. Und deshalb frag ich mich halt, obs irgendeine Art systemweite Message gibt, die bei einem Tastendruck ausgelöst wird. Baut man dann oben Code in eine Ereignisbehandlung für so eine Message ein, hat man wohl eine bessere Trefferquote. Son Programm würd mich nämlich auch mal interessieren, aber wenn das Programm manchmal meine Abkürzungen nich erwischt, dann bringts mir ja auch nix... Bis dann, S - tefano |
Ich hab dein Problem verstanden, das hatte ich ja auch...
Naja, aber man braucht wahrscheinlich doch nicht, so wie ich es oben geschrieben habe, für jede Taste einen einzelnen Hook. Ein kompletter Tastaturhook müsste nämlich seine Dienste erfüllen... Hier im Forum müsste aber schon stehen, wie man einen solchen Tastaturhook bastelt... |
Jo, habs gefunden.
Muss es mir mal genauer ansehen. |
verstehe dat Problem evtl nicht ABER:
Dass Ihr eine Nachricht bekommt, wenn eine Tate gedrückt wird setze ich jetzt mal voraus. Außerdem setze ich voraus, dass Ihr wisst um welche Taste es sich handelt. Wo ist dann dat Problem die Tasten in einem String zwischen- zuspeichern und bei jedem neuen Empfang den String auf eine Abkürzung zu überprüfen? :freak: Da brauch man dann auch nix mit Timern und so... Achso...der String muss natürlich dann gelöscht werden, wenn eine Textausgabe erfolgte, die endgültig ist, also eine Abkürzung in einen Text umgewandelt wurde, oder halt ein Buchstabe als solcher feststeht. Gruß Minz |
Das Problem liegt nicht da wo dus beschrieben hast, sondern ganz an der "Wurzel", die du auch in deinem Post erwähnt hast.
Das Problem ist halt eine Message zu bekommen wann eine Taste gedrückt worden ist, man will ja darauf reagieren. Aber das geht halt mit Hooks. Also ist das Problem fast gelöst. Nur, dass es so wie in Assarbads Tutorial beschrieben unter NT- Systemen nich geht. Zumindest bei mir nich, weil in meiner Windows- Unit die entsprechende Hook- Definition fehlt. |
Achja, wo wir dabei gerade sind.
Könnte mal bitte einer von euch in seiner Windows- Unit die Definition von WH_KEYBOARD_LL nachschlagen und posten? Wie gesagt, bei mir fehlt sie. Müsste wie folgt aussehen:
Delphi-Quellcode:
Nur halt mir KeyBoard_ll anstatt KeyBoard.
{$EXTERNALSYM WH_KEYBOARD}
WH_KEYBOARD = 2; Hoffe mal dass es überhaupt geht, einfach die Definition nachzutragen. Aber nen Versuch ist es wert, oder? Oder sollte man das lieber lassen und nicht riskieren seine WindowsUnit zu zerschießen? Danke schonmal, S - tefano |
Moin Stefano,
Du musst dafür ja nicht Deine Windows.pas anpassen, es genügt ja schon, wenn Du Dir eine Unit erstellst, in der Du dann solche Ergänzungen einbindest. Das ExternalSym brauchst Du auch nur, wenn die Unit im C++ Builder benutzt werden soll, ansonsten reicht einfach eine Konstantendeklaration.
Delphi-Quellcode:
diese Konstante funktioniert aber nur unter NT, W2K und XP.
const // aus WINUSER.H
WH_KEYBOARD_LL = 13; |
Hi,
danke erstmal für die Definition, habs dann mal in die ProgrammUnit eingebunden. Aber irgendwie gehts immernoch nicht... Diese Funktion habe ich aus einem anderen Tutorial (irgendwo bei Google gefunden) rausgenommen und so modifiziert, dass ich erstmal zum Gucken was ich da empfange, bevor ich mich an die "Abkürzungsverlängerung" mache, die gedrückten Tasten in eine Stringlist schreibe und später auf Buttonklick in ein Memo übertrage:
Delphi-Quellcode:
Key ist ein globaler Integer, list ist meine Stringlist die ich zuvor initialisiert habe.
function hookproc(ncode: integer; wpara: wparam; lpara: lparam): longint; stdcall;
begin Result := 0; if ncode=Hc_action then begin if (Peventmsg(Lpara).message = wm_keydown) or (Peventmsg(Lpara).message = wm_syskeydown) then begin key:= Peventmsg(Lpara).paraml; key:= Peventmsg(lpara).paramH; key:= mapvirtualkey(key,0); universalkey(key); //gibt String für den jeweiligen key-Integer in keystr aus //den keystring in die Stringlist schreiben if keystr = '[ENTER]' then list.Add(keystr) //wenn Enter gedrückt wurde, neue Zeile anfangen, else list[list.Count - 1]:= list[list.count - 1] + keystr; //sonst einfach anhängen end; if peventmsg(lpara).message = wm_keyup then begin key:= Peventmsg(Lpara).paraml; key:= mapvirtualkey(key,0); end; end; end; Was ich an dieser Funktion schon die ganze Zeit fragwürdig finde ist, warum key drei mal hintereinander verschiedene Werte zugewiesen werden. Als ich den Hook noch mit WH_KEYBOARD gesetzt hab, war es noch so, dass das Programm durch die zweite If-Abfrage durchkam (die wo geguckt wird, ob eine Taste oder eine Systemtaste gedrückt wurde) durchkam, dann aber die Funktion mapvirtualkey eine 0 zurückgab (weil das, was key vorher zugewiesen wurde, fast immer größer als 1000 war, aber auf jeden Fall immer größer als die ASCII- Range). Deswegen wurde auch jede Taste als Pause erkannt, weil universalkey der 0 die Taste Pause zuweist. Mit WH_KEYBOARD_LL geht das Programm aber komplett an der zweiten if- Abfrage vorbei, es wird also durch den Tastendruck über den Hook zwar die Funktion ausgeführt, aber der Tastendruck wird dann als solcher nicht mehr erkannt. So setze ich den Hook:
Delphi-Quellcode:
Ist irgendwo irgendwas falsch? Oder gibts in der hookproc auch noch Teile, die so nur unter 9x funktionieren können?
setwindowshookex(WH_KEYBOARD_LL, hookproc, Hinstance, 0);
Hoffe ihr könnt was finden. Bis dann, S - tefano |
Kann doch nich sein, dass das keiner weiß...
|
Moin Stefano,
das Problem dürfte schlicht sein, dass wParam und lParam bei WH_KEYBOARD_LL ganz andere Daten enthalten als bei WH_KEYBOARD. So wie die Hookproc aussieht könnte das zwar berücksichtigt sein, aber wie ist z.B. PEventMsg deklariert? |
Hi,
is das jetz ne direkte Frage an mich, oder eher ne r(h)et(h)orische Frage? PEventMsg steht in meiner Windows- Unit als Pointer auf TEventMsg, das wiederum der Datenstruktur der EvenMsg, die im Windows SDK nachzulesen ist entspricht. Und diese Strutkur hat die Eigenschaften lParam, hParam und message (wenn ich mich nich vertue). Ich hab mir jetzt mal zum Probieren nen Breakpoint gesetzt um mal zu gucken was da während der Hook sich meldet in der Funktion so passiert. Und irgendwie sind jetzt die Messages, die in der If- Abfrage geprüft werden weder wm_keydown noch wm_syskeydown. Sind wieder irgendwelche Zahlen, meistens zwischen 9000 und 10000, und wenn ich die Überprüfung weglasse wird jede Taste als "Pfeilnachoben" erkannt. Kann denn die Datenstruktur dieses lParams oder von was auch immer denn unter NT- Systemen wirklich so anders sein? Und wenn ja... warum wird sowas nirgendwo (weder in (zumindest meiner) Delphi Hilfe, noch im Netz (jedenfalls auf den 23 Schrott-Seiten die Google mir heute reingewürgt hat)) anständig dokumentiert? grrrr. So langsam bin ich am Verzweifeln. Hätte so tolle Sachen heute machen können, aber nein, ich Schlaubi versuche ausgerechnet heute meinen Einstieg in diese verfluchten Hooks. :evil: :cry: :evil: :cry: :evil: :cry: :evil: :cry: :evil: Hoffe von Herzen, dass einem von euch was einfällt, was mich weiterbringt. Denn nachdem ich heute durch das Niemandsland der Prono- zugemüllten Seiten, vermient mit Dialern und furchtbaren Designs und noch schlimmeren Usern, die absolut nicht in der Lage sind sich verständlich auszudrücken bzw. auf Fragen zu reagieren, bin ich dieses Mal zu dem absolut ernsthaften Schluss gekommen, dass AUQ und die Praxis die absolut unangefochten besten Delphi- Communities in deutscher Sprache sind. Ich hoffe, dass (wenn meine oben erwähnte Verzweiflung das nicht schon bewirkt hat) spätestens diese Liebeserklärung an dieses Forum einen Ansporn bietet, einem armen unschuldigen Kerl der im Glauben ist einen ganzen Tag verschwendet zu haben, zu helfen. Danke im Voraus, S - tefano |
Moin Stefano,
also in meinem PSDK heist die Struktur für WH_KEYBOARD_LL aber KBDLLHOOKSTRUCT OK, da die Länge der Struktur, und die Aufteilung gleich sind, dürfte das nichts ausmachen. Sollte dann so deklariert werden: ( mit // das entsprechende Feld von TEventMsg)
Delphi-Quellcode:
Vergleich doch mal, ob auch alles überall dort steht, wo's soll.
type
PKBDLLHOOKSTRUCT = ^KBDLLHOOKSTRUCT; KBDLLHOOKSTRUCT = packed record vkCode : DWORD; // message scanCode : DWORD; // ParamL flags : DWORD; // ParamH time : DWORD; // time dwExtraInfo : DWORD; // hwnd end; ggf. könntest Du ja vielleicht mal die Sourcen anhängen. |
Hi,
hey, das is cool. Also erstmal ist kurz zu erwähnen dass der obige Code (also der von "meiner" HookProc) unter 9x prima läuft, das also alles tatsächlich daran liegt, dass XP NT basierend ist. Jetz hab ich durch Rumprobieren rausgefunden, dass bei dieser LL-Struktur die Eigenschaft vkCode den ASCII-Code des gedrückten Zeichens enthält. Irgendwie scheints zwar so zu sein, dass die Eigenschaften dieser Struktur unter der 9x- Struktur so heißen würden wie in deinen Kommentaren, aber sie enthalten andere Daten. Unter 9x enthielt message ja ganz normal die Message, ob keyup, keydown oder sonstwas, und hier wie gesagt den ASCII- Code. Deshalb hats auch zuerst nich geklappt, einfach den Code von Oben zu nehmen und einfach nur die Eigenschaften die angesprochen werden umzubenennen. Ich frag mich nur, aus welcher Eigenschaft man jetz die Message lesen kann, um spezifisch auf Drücken und Loslassen reagieren zu können. Meine Hilfe kann mir dazu ja schlecht was sagen, kannte sie ja nichtmal diese Datenstruktur. Und zu time: Kann man daraus auslesen, wie lange eine Taste gedrückt gehalten wird? Könnt mir nicht vorstellen wie, weil doch nach diesem Wiederholungsintervall von Windows (also wenn man einen Buchstaben länger gedrückt hält, erscheint er im Endeffekt so oft wie der Intervall in die Zeit des Gedrückthaltens reinpasst) der Hook wieder aufgerufen wird. Aber wichtig wäre mir erstmal rauszufinden welche Eigenschaft ich für die Message auslesen muss. Wenn man nochn bissken mit den vkCodes rumprobiert, findet man auch die Werte raus, die so Sachen wie Shift und so wiedergeben. 160 lShift, 161 rShift, 162 lStrg, 163 rStrg, 164 Alt, 165 AltGr (wobei vor 165 immer ein 162 mitkommt). Is halt nur wie gesagt ohne die Unterscheidung für keyup und keydown blöd, weil man sonst für jeden Tastendruck zwei identische Werte bekommt. Aber nichts desto Trotz: VIELEN DANK!!!!! :bouncing4: :firejump: :bounce1: :bounce2: :hello: :bounce2: :bounce1: :firejump: :bouncing4: Hast mir ganz schön weitergeholfen mit der Datenstruktur. Insgeheim machts mich ja schon ein Bisschen stolz den größten Teil (halt bis auf wm_keyup und wm_keydown) selbst rausgefunden zu haben... Würd mich freuen, wenn sich auch dieses Problem aus dem Weg schaffen ließe. Bis dann und nochmal danke, S - tefano |
Moin Stefano,
am besten schaust Du Dir die Struktur mal im MSDN oder im PSDK an. Da sind die Felder alle erklärt. Das mit dem AltGr ist auch leicht zu erklären: Es werden unmittelbar nacheinander die Codes für CTRL und ALT generiert AltGr selber hat keinen. Warum man trotzdem mit AltGr+Del keinen CTRL+ALT+DEL simulieren kann ich mir nicht so ganz erklären, es scheint da noch mehr mitzuspielen. |
Hi,
also dieses MSDN is schon ne feine Institution... Zitat:
Kann ich das einfach über flags.LLKHF_UP ansprechen, oder muss ich sowas machen wie
Delphi-Quellcode:
? Also schonmal vorweg, durch Rumprobieren hab ich rausgefunden dass die obigen zwei Sachen nich gehen.
if flags[7] = 0 then ...
Ich kann allerdings einfach die flag an sich abfragen. Wenn die Taste gedrückt wird, kommt ganz normal 0, aber wenn man sie loslässt 128. Warum? Und. Wie kann ich denn überhaupt die verschiedenen Flags voneinander unterscheiden? Bis dann, S - tefano |
Moin Stefano,
Zitat:
Um an die verschiedenen enthaltenen Informationen zu gelangen, muss man die jeweils interessanten Bits ausmaskieren. Ausmaskieren heisst: Alle Bits bis auf das zu prüfende (oder die zu prüfenden) werden auf 0 gesetzt. Zitat:
Um nur dieses zu erhalten, und die anderen auf 0 zu setzen, muss das Feld Flags mit einem Wert logisch und (AND) verknüpft werden, der nur an dieser Stelle eine 1 enthält (Hex: 00000080). Da LLKHF_UP dieses tun soll, gehe ich mal davon aus, dass es den genannte Wert enthält.
Delphi-Quellcode:
Jetzt klärt sich wohl auch das pendeln zwischen 0 und 128. 80 Hex ist 128 Dezimal (16*8)
if (flag and LLKHF_UP) = LLKHF_UP then // Taste wurde losgelassen
else // Taste wurde gedrückt Dieses ausmaskieren von Bits findest Du an vielen Stellen, z.B. auch bei den FileAttributes wenn Du FindFirst verwendest (faDirectory wäre beispielsweise so eine Maske). |
Hi,
ahsooo :idea: , ich glaub so langsam komme ich dahinter. Allerdings gibts damit 2 Probleme. Zum einen ist meinem Delphi das Flag LLKHF_UP nicht bekannt. Ich habs dann in meinem Projekt als Konstante mit Wert 7 deklariert (so wegen 7tes Bit). Wenn ich mir dann zum Testen mal den Wert dieses Flags ausgeben lasse
Delphi-Quellcode:
(was hoffentlich richtig ist), wird mir bei beiden Ereignissen (beim Drücken und Loslassen) eine Null angezeigt.
showmessage(inttostr(PKBDLLHOOKSTRUCT(lparam).flags and LLKHF_UP));
Hab ich bei der Flag- Deklaration irgendwas falsch gemacht, oder Frage ich das Flag noch falsch ab? Bis dann, S - tefano |
Moin Stefano,
wie ich es auch schon geschrieben habe: Das Bit 7 hat den dezimalen Wert 2 hoch 7 also 128 oder $80 Daher kommt doch auch der Wechsel zwischen 0 und 128 den Du beobachtet hast. (Bit 6 dann 2 hoch 6 usw.) Was die Deklaration derartiger Konstanten angeht: Wenn Du über eine entsprechende Anbindung verfügst, lade Dir mal das PSDK runter. Da sind dann auch Header Dateien dabei, in denen solche Konstanten deklariert sind. In diesem speziellen falle wäre das allerdings nicht mal notwendig, da die Doku die Wert indirekt schon angibt. Verbinde einfach mal die Information welches Bit was angibt, mit der Information welche Konstante welche Bits anspricht. Daraus ergibt sich dann zwangsläufig, welche Konstante welchen Wert haben muss. Die Aktion mit dem ausmaskieren noch mal etwas sichtbarer (alle Angaben jetzt im Binärformat, auf 8 Bit reduziert)
Code:
Der eingelesene Wert (Beispiel) : 10111001
Die Konstante LLKHF_UP : 10000000 Beide mit AND verknüpft : 10000000 ! !--> Bit 0 !---------> Bit 7 |
Hi,
hmmmmhmmmmhmmmm :? :? :? :? :? Also ich sag mal so. Wo da jetz der Fehler lag, hab ich nach einigen Überlegungen begriffen. Funktioniert jetz auch, und ich weiß auch warum. Aber irgendwie find ichs ja schon schade, dass ich mir ziemlich sicher bin dass ich auf sowas niemals selber kommen werde... Aber mit der Konstante als $80 läufts jetz.
Delphi-Quellcode:
Und obiger Code funktioniert endlich wunderbar.
if (PKBDLLHOOKSTRUCT(lparam).flags and LLKHF_UP) = LLKHF_UP then
showmessage(keystr + ' UP') else showmessage(keystr); Also nochmal vielen Dank für die Hilfe, S - tefano |
Moin Stefano,
bitte, gerne. Zitat:
Was dabei helfen kann ist die Beschäftigung mit dem Binärsystem, und logischen Verknüpfungen/Operatoren (and, or, xor, not, shl, shr). Da dieses Verfahren in der API öfter eingesetzt wird, kann's nicht schaden sich mal damit zu beschäftigen. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:39 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz