Frage zu Simulation von Tastatureingaben
-
Hallo, ich habe mir gerade diesen Artikel durchgelesen:
So soll man also Tastatureingaben Simulieren:
1. 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. 2. 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). 3. 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.
Beispiel:
void Simulate( string key ); int main() { call FindWindow call GetDC for(int i=0;i<10;i++) // 10 mal a und b Tastendruck simulieren { Simulate( a ); // drücke a Simulate( b ); // drücke b } return 0; } void Simulate( string key ) { //Pseudocode call GetCurrentThreadId call GetWindowThreadProcessId call AttachThreadInput mit true // attach call SetFocus call SendInput AttachThreadInput mit false // detach }
Nun zu meiner Frage:
Kann ich die Funktion Simulate(); so verwenden oder ist es ehr schlecht da ich jedes mal wenn ich ein Tastendruck simulieren möchte AttachThreadInput aufrufe?
Früher hatte ich mich schon mal mit dem Thema Tastatureingaben an Fenster senden beschäftigt, da bekam ich ab und zu Probleme wenn ich nach jedem Tastendruck nicht AttachThreadInput mit false (für detach) aufgerufen habe.
Wie seht ihr das?
-
So ich hab nochmal etwas alten Code ausgekramt und rumgetestet:
Das Programm Simuliert eine Tastatur Eingabe im Editor.
Es soll 8 mal die Taste a gedrückt werden.
Manchmal werden nur 3 a´s an den Editor geschickt und manchmal aber auch 8.
Wieso funktioniert das so nicht richtig?#include <windows> #include <iostream> using namespace std; void Simulate( HWND hwnd ); int main(int argc, char* argv[]) { Sleep(2000); HWND hwnd = FindWindow (0,"Unbenannt - Editor"); if ( hwnd == 0 ) { cout<<"FindWindow error code:"<<GetLastError(); } for(int i=0;i<8;i++) { Simulate(hwnd); } system("PAUSE"); } void Simulate(HWND hwnd) { bool detach = false; bool Attach = false; DWORD getThreadID; DWORD getThreadProcessID; getThreadID = GetCurrentThreadId(); getThreadProcessID = GetWindowThreadProcessId(hwnd,0); Attach = AttachThreadInput( getThreadID , getThreadProcessID , true ); if ( Attach == 0){ cout<<"AttachThreadInput error code: "<<GetLastError()<<endl; system("PAUSE"); return ; } hwnd = SetFocus( hwnd ); if (hwnd == 0 ){ cout<<"SetFocus error code: " << GetLastError()<<endl; system("PAUSE"); return ; } INPUT data; data.type = INPUT_KEYBOARD; data.ki.wVk = 0x41; data.ki.dwFlags = KEYEVENTF_EXTENDEDKEY,KEYEVENTF_KEYUP; data.ki.time =0; data.ki.dwExtraInfo = 0; SendInput (1 , &data, sizeof(data)); // Taste drücken data.ki.dwFlags = KEYEVENTF_KEYUP; SendInput (1 , &data, sizeof(data)); // Taste loslassen detach = AttachThreadInput( getThreadID , getThreadProcessID , false ); if ( Attach == 0){ cout<<"AttachThreadInput detach error code: "<<GetLastError()<<endl; system("PAUSE"); return ; } }
-
Ach mist,
data.ki.dwFlags = KEYEVENTF_EXTENDEDKEY,KEYEVENTF_KEYUP; <-- Ohne das KEYEVENTF_KEYUP
dann geht es. Erst mal meinen alten Code Schnippsel verbessern.
Aber nochmal zu meiner Frage kann man das so machen oder ist das er unsinnig?
-
Geht, doch irgendwie nicht immer -.-
4 mal hintereinander geht es dann nicht mehr.Es geht erst wieder wenn ich selbst die Taste a einmal drücke, ich weiss echt nicht was da dran falsch sein soll ... Hat wer eine Idee?
-
Hat den keiner eine Idee was ich da falsch mache?
-
mMn. müsste folgendes funktionieren:
* attach thread input
* set focus
* detach thread input
* send input
* send input
* send input
...
-
KbdDrv schrieb:
Hat den keiner eine Idee was ich da falsch mache?
du musst zuerst eins ohne "up" schicken (=taste runterdrücken), und dann eins mit "up" (=taste loslassen).
dann wieder runterdrücken, wieder loslassen.
usw.
und was soll das KEYEVENTF_EXTENDEDKEY, wenn du bloss ein 'a' schicken willst?
-
Hallo, danke für die Anwort
So sieht jetzt mein SendInput aus ? Aber es funktioniert noch immer nur sporadisch.
Und warum soll man AttachThreadInput mit false aufrufen bevor SendInput kommt?
INPUT data; data.type = INPUT_KEYBOARD; data.ki.wVk = KeyInHex; data.ki.dwFlags = 0; data.ki.time =0; data.ki.dwExtraInfo = 0; SendInput (1 , &data, sizeof(data)); // Taste drücken data.ki.dwFlags = KEYEVENTF_KEYUP; SendInput (1 , &data, sizeof(data)); // Taste loslassen
Mein ganzes Programm:
#include <windows> #include <iostream> using namespace std; void Simulate( HWND hwnd, unsigned KeyInHex ); int main(int argc, char* argv[]) { Sleep(2000); HWND hwnd = FindWindow(0,"Unbenannt - Editor"); if ( hwnd == 0 ) { cout<<"FindWindow error code:"<<GetLastError(); system("PAUSE"); return 0; } Simulate(hwnd,0x41); Simulate(hwnd,0x42); Simulate(hwnd,0x43); Simulate(hwnd,0x44); system("PAUSE"); } void Simulate(HWND hwnd, unsigned KeyInHex) { bool detach = false; bool Attach = false; DWORD getThreadID; DWORD getThreadProcessID; getThreadID = GetCurrentThreadId(); getThreadProcessID = GetWindowThreadProcessId(hwnd,0); Attach = AttachThreadInput( getThreadID , getThreadProcessID , true ); if ( Attach == 0){ cout<<"AttachThreadInput error code: "<<GetLastError()<<endl; system("PAUSE"); return ; } hwnd = SetFocus( hwnd ); if (hwnd == 0 ){ cout<<"SetFocus error code: " << GetLastError()<<endl; system("PAUSE"); return ; } detach = AttachThreadInput( getThreadID , getThreadProcessID , false ); if ( Attach == 0){ cout<<"AttachThreadInput detach error code: "<<GetLastError()<<endl; system("PAUSE"); return ; } INPUT data; data.type = INPUT_KEYBOARD; data.ki.wVk = KeyInHex; data.ki.dwFlags = 0; data.ki.time =0; data.ki.dwExtraInfo = 0; SendInput (1 , &data, sizeof(data)); // Taste drücken data.ki.dwFlags = KEYEVENTF_KEYUP; SendInput (1 , &data, sizeof(data)); // Taste loslassen }
-
KbdDrv schrieb:
Und warum soll man AttachThreadInput mit false aufrufen bevor SendInput kommt?
Das hab ich vielleicht ungünstig formuliert.
Ich meinte nur es müsste auch funktionieren wenn man AttachThreadInput(false) bereits vorher macht.
AttachThreadInput() ist nur nötig damit SetFocus() funktioniert. SendInput() ist nicht von AttachThreadInput() abhängig, und wenn der Fokus vorher korrekt umgesetzt wurde...Funktioniert es denn zuverlässig wenn du in er 2 Sekunden Pause das Notepad Fenster selbst aktivierst (anklickst)?
-
Wenn ich selber in das Editor Fenster klicke dann funktioniert alles ohne Probleme.
Ebenso wenn ich SetForegroundWindow anstatt SetFocus verwende, dann funktioniert das Programm auch ohne Probleme da muss ich dann auch nicht erst selbst in das Fenster klicken.
Mache ich irgendwas falsch oder ist SetFocus eine verbuggte Funktion.
#include <windows> #include <iostream> using namespace std; void Simulate( HWND hwnd, unsigned KeyInHex ); int main(int argc, char* argv[]) { Sleep(2000); HWND hwnd = FindWindow(0,"Unbenannt - Editor"); if ( hwnd == 0 ) { cout<<"FindWindow error code:"<<GetLastError(); system("PAUSE"); return 0; } for(int i=0;i<3;i++){ Simulate(hwnd,0x41); Simulate(hwnd,0x42); Simulate(hwnd,0x43); Simulate(hwnd,0x44); } system("PAUSE"); } void Simulate(HWND hwnd, unsigned KeyInHex) { bool detach = false; bool Attach = false; bool foreground = false; DWORD getThreadID; DWORD getThreadProcessID; getThreadID = GetCurrentThreadId(); getThreadProcessID = GetWindowThreadProcessId(hwnd,0); Attach = AttachThreadInput( getThreadID , getThreadProcessID , true ); if ( Attach == 0){ cout<<"AttachThreadInput error code: "<<GetLastError()<<endl; system("PAUSE"); return ; } /*/ Mit SetFocus funktioniert das Programm nur sporadisch -.- hwnd = SetFocus( hwnd ); if (hwnd == 0 ){ cout<<"SetFocus error code: " << GetLastError()<<endl; system("PAUSE"); return ; } /*/ foreground = SetForegroundWindow(hwnd); if ( foreground == false ){ cout<<"SetForeground error code: " << GetLastError()<<endl; system("PAUSE"); return ; } INPUT data; data.type = INPUT_KEYBOARD; data.ki.wVk = KeyInHex; data.ki.dwFlags = 0; data.ki.time =0; data.ki.dwExtraInfo = 0; SendInput (1 , &data, sizeof(data)); // Taste drücken data.ki.dwFlags = KEYEVENTF_KEYUP; SendInput (1 , &data, sizeof(data)); // Taste loslassen detach = AttachThreadInput( getThreadID , getThreadProcessID , false ); if ( Attach == 0){ cout<<"AttachThreadInput detach error code: "<<GetLastError()<<endl; system("PAUSE"); return ; } }
-
Naja verbuggt ist relativ.
Das is ein Schutzmechanismus von Windows, damit nicht dauernd ein lästiges Programm den Fokus klaut. Grundsätzlich sehr sinnvoll, nur immer dann elendig lästig, wenn man drumrumarbeiten muss.Ich hatte kürzlich nen ähnlichen Fall, nur musste ich ein Fenster zuverlässig in den Vordergrund zwingen (der Input-Fokus wäre egal gewesen) - egal wie wild der User in ein anderes Fenster reingeklickt und/oder reingetippt hat.
Funktioniert hat bei mir letztlich nur BringWindowToTop. SetForegroundWindow/ActivateWindow/SetFocus waren *nicht* ausreichend. Weiss der Geier wieso.
-
Das mit SetFocus ist kein Schutzmechanismus und auch nicht verbuggt. Jeder Thread kann sein eigenes Focus Fenster haben und das ändert sich eben nicht wenn ein Threadcontext wechsel stattfindet...
Viele Funktionen liefern threadlokale infos. GetFocus/SetFocus gehören dazu.
Genauso wie GetCurrentDirectory prozessbezogen ist und eben auch nicht für alle Prozesse gilt.
-
Aber warum funktioniert das Programm nur sporadisch?
Dann muss ich da ja irgendwas falsch machen? Aber was?
Ich versteh echt nicht wieso das mit SetFocus nicht funktioniert.Das muss doch irgeeennddwiieee gehen.
-
Ich kann es nicht genau erläutern, es scheint aber so zu sein, dass keine richtigen Tastaturevents erzeugt werden. Es sieht so aus, als ob in einem kleinen Intervall festgestellt wird, ob die jeweilige Taste gedrückt ist oder nicht. Und wenn nach dem Tastendruck das Loslassen zu schnell kommt wird es vom Programm nicht gemerkt.
(Ich habe damit lange experimentiert)Wenn du eine Pause zwischen dem Drücken und dem Loslassen der Taste einbaust, sollten alle Tastendrücke erkannt werden. Also in etwa so:
INPUT data; data.type = INPUT_KEYBOARD; data.ki.wVk = KeyInHex; data.ki.dwFlags = 0; data.ki.time =0; data.ki.dwExtraInfo = 0; SendInput (1 , &data, sizeof(data)); // Taste drücken Sleep(60); // <- neu data.ki.dwFlags = KEYEVENTF_KEYUP; SendInput (1 , &data, sizeof(data)); // Taste loslassen
Besonders Programmen, die zu wenig fps haben, "übersehen" häufiger so simulierte Tastendrücke.
Warum aber keine Tastaturevents produziert werden die erst einmal zwischengespeichert werden kann ich mir nicht erklären. Eigentlich sollte es so sein. Es ist aber nicht so.An einer Lösung des Problems bin ich auch sehr interessiert.
-
@Andreas XXL
Programme die die Tastatur pollen können sowas übersehen, ja.
Programme wie Notepad, die auf Window-Messages reagieren, nicht.Kann also bei ihm nicht das Problem sein, wenn er mit Notepad testet.
Passt auch nicht zum Fehlerbild, nämlich dass der Fehler verschwindet, sobald er während seines Sleep(2000) mit der Maus in das andere Fenster reinklickt.Heisst: das Problem ist ganz klar das Setzen des Fokus, und nicht das Generieren der Input-Events.
-
Ich hab die Lösung:
So funktioniert es.Hier ist auch nochmal die Lösung: http://www.c-plusplus.net/forum/289087
Ich habs mal in beide Posts geschrieben unter 2 verschiedenen Namen.
mhhm egal^^
#include <windows> #include <iostream> #define MAPVK_VK_TO_VSC 0 // Ich hab hier gerade eine veraltete Entwicklungsumgebung deswegen // das define an der Stelle ^^ using namespace std; void Simulate( HWND hwnd, unsigned KeyInHex ); int main(int argc, char* argv[]) { Sleep(2000); HWND hwnd = FindWindow(0,"Unbenannt - Editor"); if ( hwnd == 0 ) { cout<<"FindWindow error code:"<<GetLastError(); system("PAUSE"); return 0; } for(int i=0;i<3;i++) { Simulate(hwnd,0x41); Simulate(hwnd,0x42); Simulate(hwnd,0x43); } system("PAUSE"); } void Simulate(HWND hwnd, unsigned KeyInHex) { bool detach = false; bool Attach = false; bool foreground = false; DWORD getThreadID; DWORD getThreadProcessID; UINT HardwareScanCode ; getThreadID = GetCurrentThreadId(); getThreadProcessID = GetWindowThreadProcessId(hwnd,0); Attach = AttachThreadInput( getThreadID , getThreadProcessID , true ); if ( Attach == 0){ cout<<"AttachThreadInput error code: "<<GetLastError()<<endl; system("PAUSE"); return ; } hwnd = SetFocus( hwnd ); if (hwnd == 0 ){ cout<<"SetFocus error code: " << GetLastError()<<endl; system("PAUSE"); return ; } HardwareScanCode = MapVirtualKey ( KeyInHex, MAPVK_VK_TO_VSC); INPUT data; data.type = INPUT_KEYBOARD; data.ki.wVk = KeyInHex; data.ki.wScan = HardwareScanCode; data.ki.dwFlags = 0; data.ki.time =0; data.ki.dwExtraInfo = 0; SendInput (1 , &data, sizeof(data)); // Taste drücken data.ki.dwFlags = KEYEVENTF_KEYUP; SendInput (1 , &data, sizeof(data)); // Taste loslassen detach = AttachThreadInput( getThreadID , getThreadProcessID , false ); if ( Attach == 0){ cout<<"AttachThreadInput detach error code: "<<GetLastError()<<endl; system("PAUSE"); return ; } }