Überwachung von Tastatureingaben
Einleitung
Oft wird in Beiträgen aus Foren und Newsgroups die Frage gestellt, wie man systemweit Tastatureingaben aufgezeichnet bzw. auf Eingaben, die nicht unbedingt im eigenen Fenster getätigt werden, reagieren kann. Vielfach werden solche Beiträge ignoriert, da die Leser annehmen, daß der Schreiber ein Programm entwickeln will, um die Tastatur zu „überwachen“. Doch zum Ausspionieren von Tastatureingaben gibt es bereits ausreichend kostenlos erhältliche Programme im Internet, was zur Annahme veranlaßt, daß die Überwachung nicht nur zu diesem Zweck benötigt wird.
Um eine systemweite Überwachung der Tastatur zu realisieren, gibt es zwei Ansätze. Einerseits kann mit einem Timer-Steuerelements alle Millisekunden der Status aller Tasten abgefragt werden, auf der anderen Seite besteht die Möglichkeit, über einen Tastaturhook Meldungen beim Drücken einer Taste an die Visual-Basic-Anwendung weiterzuleiten. Während die erste Lösungsmöglichkeit einfach zu implementieren ist, muß bei der zweiten Möglichkeit auf eine zusätzliche Programmiersprache zurückgegriffen werden.
Tastaturüberwachung mit einem Timer-Steuerelement
Da es mit reinen Visual-Basic-Mitteln nicht möglich ist, einen systemweiten Hook zu setzen, wollen wir in unserem Beispiel der Status jeder Taste in bestimmten zeitlichen Abständen überprüfen. Zu diesem Zweck stellt das Windows-API die Funktionen GetAsyncKeyState
und GetKeyState
bereit. Diese Funktionen werden über einen Zeitgeber, dessen Intervall auf eine Millisekunde eingestellt ist, aufgerufen. Zwischen den beiden Funktionen gibt es Unterschiede, so gibt GetAsyncKeyState
sowohl den Status der Taste als auch Informationen darüber zurück, ob sich der Tastenstatus seit dem letzten Aufruf der Funktion für die Taste verändert hat.
Folgendes Listing zeigt die Deklarationen der Funktionen GetAsyncKeyState
und GetKeyState
. Über die Konstante VK_CAPITAL
kann ermittelt werden, ob ein Groß- oder Kleinbuchstabe vorliegt:
Will man lediglich prüfen, ob gerade eine Taste gedrückt ist, wobei es irrelevant ist, ob sich seit der letzten Überprüfung der Status der Taste geändert hat, dann muß nur überprüft werden, ob das höchstwertige Bit (MSB, Test mit „Verunden“ mit −32.768) gesetzt ist:
Bei einigen Projekten zur Tastaturüberwachung, die im Internet als Quellcode verfügbar sind, tritt das Problem auf, daß beim initialisieren des Aufzeichnungsvorhangs, also wenn die oben genannten Funktionen für eine Taste zum ersten Mal aufgerufen werden, ermittelt wird, daß diese gedrückt ist, obwohl dies nicht der Fall ist. Dies liegt daran, daß der Tastaturpuffer vor dem ersten Durchlauf der Tasten nicht leer ist. Damit die Anwendung korrekt funktioniert, muß zuerst der Status jeder Taste abgefragt und erst danach mit der Aufzeichnung begonnen werden. Wenn die Taste gedrückt ist, ist das höchstwertige Bit des Rückgabewertes gesetzt:
Bei jedem Feuern des Timer-Steuerelements wird für alle interessanten Tasten eine solche Überprüfung vorgenommen. Bei einigen Tasten ist es für das Protokoll interessant zu wissen, ob die Hochstelltaste gedrückt ist, um zwischen Groß- und Kleinbuchstaben zu unterscheiden. Im Parameter von GetAsyncKeyState
muß für die Taste der virtuelle Tastencode (engl.: virtual key code“) übergeben werden. Dazu kann man entweder eine der VK_*
-Konstanten benutzen oder die Visual-Basic-eigenen vbKey*
-Konstanten angeben.
Bei der Anfertigung eines Tastaturprotokolls im Textformat ist zu beachten, daß sprachspezifische Sonderzeichen (Tasten), beispielsweise Ä, im Code gesondert behandelt werden müssen. Weiters können für Funktionstasten eigene beschreibende Texte im das Protokoll angegeben werden.
Tastaturüberwachung mit einem Tastaturhook
Einen etwas fortgeschrittenen Ansatz zum Implementieren einer Tastaturüberwachung bietet ein extra dafür vorgesehener Hook. Ein Hook ist nichts anderes als eine Rückrufprozedur, die aufgerufen wird, wenn ein bestimmtes Ereignis, in unserem Fall ein Tastendruck, eintritt. Windows stellt Funktionen zum Setzen eines solchen Hooks und zur weiteren Verarbeitung bereit. Alles, was der Programmierer machen muß, ist die Bereitstellung einer passenden Rückrufprozedur, deren Aussehen vorgegeben ist. Bei Eintreten des Ereignisses, für das der Hook hinzugefügt wurde, ruft Windows nacheinander die in die Kette von Rückrufprozeduren eingehängten Prozeduren auf.
Leider unterliegen manche Hooks, darunter auch systemweite Tastaturhooks, einer Einschränkung, die eine reine Visual-Basic-Lösung verhindert. Die Rückrufprozedur muß nämlich in einer „echten“ DLL implementiert werden, etwas, das man mit Visual Basic nicht erstellen kann. Normalerweise wird man deshalb auf Visual C++ zurückgreifen, was für das zu diesem Artikel gehörende Beispiel auch getan wurde. Selbstverständlich kann man aber auch jede beliebige andere Programmiersprache benutzen, für die ein Compiler verfügbar ist, der Win32-DLLs kompilieren kann.
Die Bibliothek, in der der Hook implementiert ist, muß laut Spezifikation zwei Funktionen offen legen, nämlich eine, die den Hook setzt und eine, die den Hook wieder entfernt. Allerdings suchen wir nicht nach einer reinen C-Lösung, sondern wollen, daß eine in Visual Basic geschriebene Anwendung über die Tastaturereignisse informiert wird. Zu diesem Zweck verfügt die von der DLL exportierte Funktion zum Setzen des Hooks über einen Parameter, in dem eine Fensterzugriffsnummer übergeben werden kann. Die Bibliothek leitet dann innerhalb der Rückrufprozedur die Nachrichten an die Fensterprozedur des in Form der Zugriffsnummer übergebenen Fensters weiter. Innerhalb der Fensterprozedur eines Formulars der Visual-Basic-Anwendung muß demnach nur mehr nach der Nachricht WM_COPYDATA
Ausschau gehalten werden.
Der einfache Tastaturhook
Das Setzen dieses Hooks geschieht über einen Aufruf der API-Funktion SetWindowsHookEx
mit der Konstanten WH_KEYBOARD
als Parameterwert für idHook
. Als Rückrufprozedur wird ein Funktionszeiger auf eine in der DLL benutzerdefinierte Funktion, die den Prototypen KeyboardProc
implementiert, angegeben. Innerhalb der Rückrufprozedur wird eine Struktur des Typs COPYDATASTRUCT
mit den Informationen zum eingetretenen Tastaturereignis gefüllt und eine WM_COPYDATA
-Nachricht an die Fensterprozedur eines beliebigen Fensters gesendet. Innerhalb der Fensterprozedur können die Nachricht und die dazugehörigen Informationen verarbeitet werden.
Meherere Anwendungen können jeweils ihren eigenen Tastaturhook installieren, weshalb es innerhalb von Windows eine ganze Kette von benutzerdefinierten Rückrufprozeduren geben kann, die auf die Nachricht reagieren. Aus diesem Grund ist es erforderlich, am Ende der Rückrufprozedur die Funktion CallNextHookEx
aufzurufen, damit andere Anwendungen, die in der Aufrufkette weiter hinten gereiht sind, die Benachrichtigung erhalten.
Low-Level-Tastaturhooks
Seit Windows NT 4.0 besteht die Möglichkeit, einen Low-Level-Tastaturhook zu verwenden. Auch dieser muß in einer „echten“ DLL implementiert werden, was eine reine Visual-Basic-Lösung unmöglich macht. Die Installation eines solchen Hooks wird durch Aufrufen der API-Funktion SetWindowsHookEx
mit der Konstanten WH_KEYBOARD_LL
als Wert des Parameters idHook
durchgeführt. Die DLL muß eine Rückrufprozedur des Prototypen LowLevelKeyboardProc
implementieren, in die von Windows u. a. ein Zeiger auf eine Struktur des Typs KBDLLHOOKSTRUCT
hineingereicht wird. Durch Dereferenzieren des Zeigers gelangt man an die erweiterten Informationen zum Tastendruck.
Schlußwort
Es gibt mehrere Möglichkeiten, die Tastatur zu überwachen, von denen jede ihre Vor- und Nachteile aufweist. Will man eine Lösung, die ohne zusätzliche, nicht in Visual Basic geschriebene Komponenten funktioniert, dann muß man sich für die hier beschriebene Variante mit dem Timer-Steuerelement entscheiden. Wenn man noch zusätzliche Informationen zu den Tastaturereignissen ermitteln will, dann reicht ein einfacher Tastaturhook, etwa in einer C++-DLL implementiert, aus. Soll das Programm in der Lage sein, Tastaturereignisse zu unterdrücken bzw. zu verändern, dann wird man auch nicht um die Verwendung eines Tastaturhooks herumkommen.
Downloads
- Beispielprojekt (
KeyboardHook.zip
) Projekte im Visual-Basic-6.0- und Visual-C++-2003-Format.