Dies ist eine Ergänzung zu meinem Blog Eintrag Die Unsitte Windows interne Nachrichten zu versenden.
In einem der Kommentare wurde ich gefragt wie man es nun mit WM_KEY… Nachrichten richtig macht, weil ich auch diese Nachrichten als „intern“ definiert habe. In meinen Augen ist es unzulässig WM_KEY… Nachrichten per PostMessage oder SendMessage zu versenden. Also ist die Frage berechtigt:
Wie macht man es richtig, mit der Simulation von Tastatureingaben für andere Prozesse ❓
Ersteinmal möchte ich erklären, warum das Senden einer WM_KEY… Nachricht nicht funktioniert, oder oft genug nicht richtig funktioniert:
- Alleine wenn das Programm GetKeyState oder GetAsynchKeystate benutzt um zu ermitteln ob gleichzeitig sie Strg- oder die Umschalttaste gedrückt wird, dann funktioniert SendMessage/PostMessage nicht mehr.
- Wenn das fremde Programm Accelerator verwendet, dann wird hier auch durch senden einer WM_KEY… Nachricht der Accelerator oft genug nicht ausgelöst. Die Folge: Der gewünschte Effekt bleibt aus.
- Man bekommt schnell Probleme mit synthetisierten Zeichen, die zwei Tasteneingaben erfordern, z.B. ^ und a, für â.
- Man muss fürchterlich darauf achten, wenn ein Befehl den Fokus wechselt.
- Die Applikation hat nicht den Fokus! Alleine das kann schon Fehlverhalten auslösen, denn normalerweise kann nur eine aktive Applikation mit Fokus Tastatur- und Mauseingaben erhalten.
- WM_SYSKEY… Nachrichten werden nicht korrekt erzeugt.
Wie macht man es nun ❓
Es ist ganz einfach: Man benutzt SendInput ❗
Der Vollständigkeit halber sei hier noch die legacy Funktion keybd_event erwähnt.
SendInput reiht die Eingaben ein in die Eingabequeue für das gesamte Windows System ein und liefert diese an die aktive Applikation aus und an das Fenster, das den aktuellen Eingabefokus hat. Man kann also nicht einfach so auch das Zielfenster angeben, dass die Eingaben erhalten soll. Das merkt man schon daran, dass es kein Fensterhandle gibt, dass man als Ziel angeben könnte für SendInput.
Frage aber nun: Wie gewährleistet man, dass das richtige Programm die Tastaturnachricht bekommt ❓
Auch hier ist die Antwort relativ einfach! Zwei Dinge sind dazu nötig:
- Dass Zielfenster, bzw. den Thread in dem das Zielfenster muss ausgewählt werden, damit er diese Eingaben auch erhalten darf. Das wird durch AttachThreadInput erreicht. Denn wir wollen die Daten ja nicht an unsere Applikation senden. ❗ Bitte hinterher nicht vergessen AttatchThreadInput mit FALSE aufzurufen und den Thread wieder zu detachen.
- Man muss den Fokus korrekt setzten für das Fenster, dass die Eingaben erhalten soll!
Ist der Zielthread jetzt an die Eingabequeue angeschlossen, kann man ohne Probleme SetFocus ausführen (was ohne AttachThreadInput normalerweise auch nicht möglich wäre). - Wird jetzt SendInput ausgeführt, werden alle Nachrichten korrekt erzeugt und auch alle Commands entsprechend der Tastaturfolge ausgelöst. Auch spezielle Umlaute und Unicode Zeichne lassen sich so erzeugen.
Es ist gar nicht so schwer es richtig zu machen.
Wer mit SetFocus u.ä. arbeitet, der kann keine Tasten an ein minimiertes Fenster sendet. Niemals.
Frohes Neujahr!
Wenn ich mich nicht ganz und gar irre kann ist ein minimiertes Fenster unsichtbar und kann per Definition weder den Focus haben und auch keine Tastatureingaben erhalten und schon gar kein Childwindow innerhalb der Applikation, die minimiert ist. Das ist so…
Man mag mit WM_KEY… Nachrichten Effekte erzielen, jedoch wird man niemals gleiches Verhalten erreichen wie bei einer echten Tastatureingabe bzw. einer die durch SendInput erzeugt wurde.
Hi Martin,
ich habe die beiden Beiträge zu diesem Thema mit großem Interesse gelesen, verstehe jedoch eine Sache nicht. Du sagst, ein Fenster empfängt keine Nachrichten, wenn es nicht den Fokus hat. Wenn ich allerdings ein Fenster per Spy++ überwache, empfängt es doch verschiedene Nachrichten (z.B. WM_NCHITTEST, WM_SETCURSOR). Also, was verstehe ich denn jetzt falsch?
Ich meinte natürlich, das Fenster empfängt diese Nachrichten ohne Fokus.
Tastaturnachrichten! Darum ging es. Und diese werden nur empfangen, wenn ein Fenster den Focus hat!
Mausnachrichten und gerade Mauszeiger Nachrichten sind ein anderes Baltt!
Hmpfh! Logisch. Dann fass ich mir jetzt mal an den Kopf und überprüfe dort alles auf Vollständigkeit. Und morgen gehe ich zum Optiker…
Hallo Martin,
ich habe eine Frage zu SendInput! Löst der Aufruf der Funktion auch das erste Problem, das du beschreibst, mit GetKeyState und GetAsyncKeyState?
Ich möchte nämlich Tastatureingaben bei einem Programm simulieren, das zur Abfrage scheinbar nur GetAsyncKeyState benutzt. (Böse, böse, ich weiß, ist aber nicht von mir erstellt, kann es folglich auch nicht ändern)
Auf SendInput reagiert das nicht, auch mit keybd_event habe ich es erfolglos versucht.
Weißt du, ob es überhaupt möglich ist, einem solchen Programm Tastatureingaben vorzutäuschen, oder irgendwie die Rückgabewerte von GetAsyncKeyState zu manipulieren?
Gute Frage. Ehrlich gesagt, habe ich mir um GetAsynchKeyState und SendInput noch nie recht Gedanken gemacht… IMHO müsste GetAsncKeyState auch auf SendInput reagieren, aber ich mag mich irren.
Hier ist ja auch schon ein Konflikt, denn SendInput erzeugt einen Queue Eintrag, während ja GetAsyncKeyState den „echten“ KeyState angibt…
Ich müsste das mal testen.
Nun ja, SendMessage wirkt auf mich beruhigender, da es ein
Fensterhandle „hwnd“ mit als Argument hat, womit ich also „dem“ Fenster
und nicht „irgendeinem“ Fenster unsere Nachricht senden kann. Man
denke an einen Dialog mit Button’s, Static’s und vielleicht mehreren
Edit’s. Will man letztere auffuellen, trifft man per „hwnd“ ins richtige
Fenster. GetKeyState() wuerde ich vermeiden. Vielleicht ist auf einem
stark belasteten PC die Taste gar nicht mehr gedrueckt, bis unser
Programm endlich an die Stelle mit unserem GetKeyState() kommt. Mit der
WM_KEYxxx-Nachricht kommen wir auch die SHIFT – und CTRL – Tasten
als Bits in „lParam“ an.
1. Du irrst. GetKeyState ist ja genau der an dieser gerade bearbeiteten Nachricht gebundene Status der Tasten. Zu dem Moment als die Nachricht in die Queue kam.
2. Ob es brühgend auf Dich wirkt oder nicht. Mit SendMessage kann man keine kombinierten Zeichen übergebenund nicht alle Tastenfunktionen auslösen zudem muss kein Programm dies unterstützen, da ja geade GetKeyState nicht synchron ist.
3. Wo bitte wird in WM_KEYDOWN der Status der Shift und Ctrl Taste übermittelt? Nirgends. Wenn man also prüfen will ob Strg+C gedrückt wurde bleibt einem nur GetKeyState und dann? Kein Entwicklermerkt sich, dass eben die Strg Taste gedrückt wurde um hinter C zu verarbeiten…