Tastatur
-
Hallo zusammen,
wie kann ich mit MFC Tastaturereigniss abfangen??
mein 1ster Versuch:
Mit Wizard habe ich eine Funktion implementiert die aufgerufen wird
bei WM_KEYDOWN, beim asuführen passiert leier nicht!!!!
was mache ich da falsch??Gruß
-
Tja, dann sollte es aber eigentlich gehen.
Wie wär's mal mit etwas Beispielcode?
-
Mit dem Klassen-Assistent habe ich die Methode "OnKeyDown" mit WM_KEYDOWN verknüpft:
void CEditorDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{m_ausgabe = "Taste gedrückt!!"
UpdateData(false);
CDialog::OnKeyDown(nChar, nRepCnt, nFlags);
}--> wenn ich irgendeine Tase drücke sollte eine Ausgabe erscheinen, passiert aber nichts.
Beim debugen wirddie Methode gar nicht ausgeführt!!
-
Ich geh mal davon aus das du eher an Buchstaben gedacht hast
Dann solltest du dir mal die WM_CHAR angucken 
-
trotzdem sollte WM_KEYDOWN auslösen..

-
habe eben was von "PreTranslateMessage(MSG* pMsg)", ich glaube die sollte ich auch benutzen???
-
tss damit kannst du eine Messageverarbeitetn bevor dies die Anwendung tut...
Referenzen:
CWnd::PreTranslateMessage(MSG* pMsg)
CWnd::OnChar
CWnd::OnKeyDown
-
Schau Dir folgendes an:
WM_KEYDOWN -> OnKeyDownvoid C...Dlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { MessageBox("Taste gedrückt","Info"); CDialog::OnKeyDown(nChar, nRepCnt, nFlags); }Die interessante Frage ist nun: Reagiert die Anwendung nun auf alle Tasten unserer Tastatur mit der Ausgabe der MessageBox? Die Antwort ist nein. Bei folgenden Tasten sehen wir keine MessageBox: Esc, Enter, F10, Druck, Tab, Alt und vier Pfeiltasten.
Bastele Dir mal Folgendes zum Experimentieren:
void C...Dlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { CString str; str.Format("nChar: %d, nRepCnt: %d, nFlags: %d", nChar, nRepCnt, nFlags); MessageBox(str); if( nChar == VK_CONTROL )//VK_... testen { if( nFlags & 256 ) MessageBox("rechte Strg-Taste","Info"); else MessageBox("linke Strg-Taste","Info"); } CDialog::OnKeyDown(nChar, nRepCnt, nFlags); }Schaue in die Datei winuser.h. Dort findet man die Zusammenhänge zwischen den gültigen virtuellen Tastatur-Codes und den entsprechenden Zahlenwerten.
CWnd::OnKeyDown(...) ist nur eine Möglichkeit, Tasteneingaben auszuwerten. Eine zweite Möglichkeit besteht in der Verwertung der Nachricht WM_CHAR in der Funktion CWnd::OnChar(...). Die genaue Syntax ist:
afx_msg void CWnd::OnChar( UINT nChar, UINT nRepCnt, UINT nFlags );nChar ist der sogenannte Charactercode, nRepCnt die Zahl der Wiederholungen bei gedrückt gehaltener Taste und nFlags besteht aus folgenden Komponenten:
Value Meaning
0-15 Specifies the repeat count. The value is the number of times the keystroke is repeated as a result of the user holding down the key.
16-23 Specifies the scan code. The value depends on the original equipment manufacturer (OEM)
24 Specifies whether the key is an extended key, such as the right-hand ALT and CTRL keys that appear on an enhanced 101- or 102-key keyboard. The value is 1 if it is an extended key; otherwise, it is 0.
25-28 Used internally by Windows.
29 Specifies the context code. The value is 1 if the ALT key is held down while the key is pressed; otherwise, the value is 0.
30 Specifies the previous key state. The value is 1 if the key is down before the message is sent, or it is 0 if the key is up.
31 Specifies the transition state. The value is 1 if the key is being released, or it is 0 if the key is being pressed.Beispiel zum Experimentieren:
void C...SDI...View::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { CString str; str.Format("nChar: %d, nRepCnt: %d, nFlags: %d", nChar, nRepCnt, nFlags); MessageBox(str); CView::OnChar(nChar, nRepCnt, nFlags); }Ein ideales Werkzeug zur Verfolgung von Nachrichten ist das VC++-Tool Spy++. Stelle als Nachrichtenfilter „Tastatur“ ein und drücke vergleichend die 0 auf der Tastatur und einmal die 0 im Nummernblock.
Man sieht 6 Nachrichten:
Virtual Key Code Scan Code WM_KEYDOWN ‚0‘ 0B WM_CHAR 48 0B WM_KEYUP ‚0‘ 0B WM_KEYDOWN VK_NUMPAD0 52 WM_CHAR 48 52 WM_KEYUP VK_NUMPAD0 52Man kann die beiden Tasten sowohl über den virtuellen Tastaturcode als auch über den Scancode unterscheiden.
Man sieht hier gut den Verlauf der gesamten Nachrichtenlage. Die Taste wird gedrückt und löst die Nachricht WM_KEYDOWN aus. Die WinAPI-Funktion ::TranslateMessage(...), die man in der WinAPI-Programmierung aus der Nachrichtenschleife kennt, „übersetzt“ den Tastaturanschlag in eine entsprechende Nachricht WM_CHAR. Anschließend wird die Taste wieder losgelassen. Dies spiegelt sich in der Nachricht WM_KEYUP wieder. Über die entsprechenden Parameter kann man die Taste genau zuordnen.
Zur Gruppe „Tastatur“ gehören folgende 13 Windows-Nachrichten:
WM_CHAR WM_SYSCHAR WM_DEADCHAR WM_SYSDEADCHAR WM_KEYDOWN WM_SYSKEYDOWN WM_KEYUP WM_SYSKEYUP WM_CHARTOITEM WM_VKEYTOITEM WM_HOTKEY WM_GETHOTKEY WM_SETHOTKEYWM_DEADCHAR entsteht nur in Verbindung mit einer Nachricht WM_CHAR. Ein Beispiel ist das Zeichen ^. Teste dies mit Spy++, indem Du 2^3 eingibst. Spy++ zeigt die Abfolge der Nachrichten:
Die Eingabe von 2 bewirkt WM_KEYDOWN, WM_CHAR und WM_KEYUP.
Die Eingabe von ^ bewirkt WM_KEYDOWN, WM_DEADCHAR und WM_KEYUP.
Die Eingabe von 3 bewirkt WM_KEYDOWN, WM_CHAR, WM_CHAR und WM_KEYUP.Das doppelte WM_CHAR bei der Eingabe von 3 wird bereits bei der zweiten Eingabe durch WM_DEADCHAR vorbereitet, aber erst durch den nächsten Tastendruck als WM_CHAR ausgelöst.
Drückt man die Alt-Taste und läßt sie anschließend wieder los, so erzeugt man eine jeweils eine Nachricht WM_SYSKEYDOWN und WM_SYSKEYUP.
Tippt man die Alt-Taste und gleichzeitig die Taste A, so erzeugt man eine doppelte Nachricht WM_SYSKEYDOWN. Die entsprechenden virtuellen Tastencodes sind VK_MENU und ‚A‘. Anstelle der Nachricht WM_CHAR findet man eine Nachricht WM_SYSCHAR.
Die Nachrichten WM_CHARTOITEM und WM_VKEYTOITEM werden von einer ListBox mit dem Stil LBS_WANTKEYBOARDINPUT als Antwort auf die Nachricht WM_CHAR bzw. WM_KEYDOWN an das Elternfenster gesendet.
WM_HOTKEY ist eine spezielle Nachricht im Zusammenhang mit systemweiten Hotkeys, die durch die WinAPI-Funktion RegisterHotKey(...) erzeugt werden. WM_GETHOTKEY und WM_SETHOTKEY sind Nachrichten, die mit Hotkeys in Zusammenhang stehen, die nur einzelnen Fenstern zugeordnet sind.
Im Zusammenhang mit Tastatureingaben gibt es auch WinAPI-Funktionen, die man kennen sollte. Da gibt es z.B. die Funktion
SHORT GetKeyState( int nVirtKey /*virtual-key code*/ );Diese Funktion liefert den Zustand der angegebenen virtuellen Taste. Als Ergebnis erwartet man einen negativen Wert, wenn die Taste gedrückt ist.
In winuser.h findet man in diesem Zusammenhang folgenden Code:
/* VK_L* & VK_R* - left and right Alt, Ctrl and Shift virtual keys. * Used only as parameters to GetAsyncKeyState() and GetKeyState(). * No other API or message will distinguish left and right keys in this way. */ #define VK_LSHIFT 0xA0 #define VK_RSHIFT 0xA1 #define VK_LCONTROL 0xA2 #define VK_RCONTROL 0xA3 #define VK_LMENU 0xA4 #define VK_RMENU 0xA5Wollen wir z.B. feststellen, ob beide Strg-Tasten gedrückt sind, so kann man dies wie folgt ermitteln:
void CxxxView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { if( ( ::GetKeyState( VK_LCONTROL ) < 0) && ( ::GetKeyState( VK_RCONTROL ) < 0) ) MessageBox("Beide Strg-Tasten wurden gleichzeitig gedrückt."); CView::OnKeyDown(nChar, nRepCnt, nFlags); }Diese Funktion gibt den Zustand der Taste genau zum Zeitpunkt der Erzeugung der Nachricht an. Im vorstehenden Fall ist das genau richtig. Will man den Zustand außerhalb eines Nachrichtenhandlers abfragen, so benutzt man die Funktion
SHORT Get AsyncKeyState( int nVirtKey /*virtual-key code*/ );Damit fragt man den Zustand der Taste zum Zeitpunkt der Ausführung der Funktion ab.
Manchmal will man Tastendrucke vom Programm aus simulieren. Hierzu ist die veraltete aber einfach einzusetzende Funktion
void keybd_event ( BYTE bVk, // virtual-key code BYTE bScan, // hardware scan code DWORD dwFlags, // function options ULONG_PTR dwExtraInfo // additional keystroke data );geeignet. Nachfolgend konkrete Beispiele für den Einsatz von keybd_event(…):
keybd_event( VK_RETURN, 0, 0, 0 ); keybd_event( VK_BACK, 0, 0, 0 );Seit Windows NT empfiehlt Microsoft folgende Funktion:
UINT SendInput ( UINT nInputs, // count of input events LPINPUT pInputs, // array of input events int cbSize // size of structure );Im Fall von SendInput(…) ist das deutlich komplizierter als bei keybd_event(…). Der zweite Parameter ist ein Zeiger auf die Struktur INPUT:
typedef struct tagINPUT { DWORD type; // Auswahl: INPUT_KEYBOARD, INPUT_MOUSE, INPUT_HARDWARE union { MOUSEINPUT mi; KEYBDINPUT ki; HARDWAREINPUT hi; }; } INPUT, *PINPUT;Die Struktur KEYBDINPUT wiederum ist wie folgt aufgebaut:
typedef struct tagKEYBDINPUT { WORD wVk; // virtual-key code WORD wScan; // scan code DWORD dwFlags; // function options DWORD time; ULONG_PTR dwExtraInfo; } KEYBDINPUT, *PKEYBDINPUT;Damit die Vorgehensweise beim Erzeugen von Tastaturereignissen mittels Programm weitgehend klar wird, sollte man sich eine Basis für konkrete Versuche schaffen, z.B.:
void CTastaturSDI001View::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { if ( (::GetKeyState( VK_RCONTROL ) < 0) && (::GetKeyState( VK_LCONTROL ) < 0) ) MessageBox("Beide Strg-Tasten gedrückt."); CView::OnKeyDown(nChar, nRepCnt, nFlags); }Nun kann man diese beiden Tastendrucke „synthetisch“ erzeugen, nämlich mit einem linken Mausklick in den Anwendungsbereich. Man fügt die Nachricht WM_LBUTTONDOWN und die entsprechende Ereignishandler-Funktion ein:
void CTastaturSDI001View::OnLButtonDown(UINT nFlags, CPoint point) { keybd_event(VK_LCONTROL, 0, 0, 0); keybd_event(VK_RCONTROL, 0, 0, 0); CView::OnLButtonDown(nFlags, point); }Beim Ausprobieren stellt man jedoch fest, dass das so nicht funktioniert. Beim „echten“ Drücken der beiden Strg-Tasten ist alles in Ordnung. Jedoch beim linken Mausklick gibt es keinen Mucks. Das liegt vielleicht daran, dass man die Abfrage mit ::GetKeyState(...) durchführen. Also fügt man zur Kontrolle noch eine MessageBox ein, die zeigt, ob man überhaupt die Nachricht WM_KEYDOWN erzeugt:
void CTastaturSDI001View::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { MessageBox("WM_KEYDOWN ausgelöst"); if ( (::GetKeyState( VK_RCONTROL ) < 0) && (::GetKeyState( VK_LCONTROL ) < 0) ) MessageBox("Beide Strg-Tasten gedrückt."); CView::OnKeyDown(nChar, nRepCnt, nFlags); }Das funktioniert beim Mausklick. Man sieht die Meldung, die zeigt, dass man die Nachricht WM_KEYDOWN ohne Berühren der Tasten ausgelöst hat.
Das Hemmnis liegt in diesem speziellen Fall darin begründet, dass man die Funktion GetKeyState(...) verwendet. Daher fragt man den Tastenzustand nun mit GetAsyncKeyState(...) ab. Man behebt hierbei noch ein Problem, wenn man andere Programme parallel betreibt. Man hat die Strg-Tasten per Programm zwar gedrückt, aber nicht wieder losgelassen. Das ergänzt man ebenfalls sofort. Der Code sollte nun wie folgt aussehen:
void CTastaturSDI001View::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { if ( (::GetKeyState( VK_RCONTROL ) < 0) && (::GetKeyState( VK_LCONTROL ) < 0) ) MessageBox("Beide Strg-Tasten gedrückt.", "Sync in OnKeyDown"); if ( (::GetAsyncKeyState( VK_RCONTROL ) < 0) && (::GetAsyncKeyState( VK_LCONTROL ) < 0) ) MessageBox("Beide Strg-Tasten gedrückt.","Async in OnKeyDown"); keybd_event(VK_LCONTROL, 0, KEYEVENTF_KEYUP, 0); keybd_event(VK_RCONTROL, 0, KEYEVENTF_KEYUP, 0); CView::OnKeyDown(nChar, nRepCnt, nFlags); } void CTastaturSDI001View::OnLButtonDown(UINT nFlags, CPoint point) { keybd_event(VK_LCONTROL, 0, 0, 0); keybd_event(VK_RCONTROL, 0, 0, 0); /* if ( (::GetAsyncKeyState( VK_RCONTROL ) < 0) && (::GetAsyncKeyState( VK_LCONTROL ) < 0) ) MessageBox("Beide Strg-Tasten gedrückt.", "Async in OnLButtonDown"); */ CView::OnLButtonDown(nFlags, point); }Nun ist funktionell alles in Ordnung. Man klickt und löst damit das virtuelle Niederdrücken beider Strg-tasten aus. Im Ereignishandler OnKeyDown(...) fragt man den Zustand der beiden Tasten aktuell ab, um sie dann wieder virtuell loszulassen. Experimentiere mit diesem Programm, damit Du ein Gefühl für die Abläufe erhälst. Die Abfrage der Tasten kann auch direkt in OnLButtonDown(...) erfolgen.
Die neuere Funktion SendInput(...) anstelle keybd_event(...):
#include <winable.h> void CTastatur001View::OnLButtonDown(UINT nFlags, CPoint point) { KEYBDINPUT ki1, ki2; ki1.wVk = VK_LCONTROL; ki1.wScan = 0; ki1.dwFlags = 0; ki1.dwExtraInfo = 0; ki1.time = 0; ki2.wVk = VK_RCONTROL; ki2.wScan = 0; ki2.dwFlags = 0; ki2.dwExtraInfo = 0; ki2.time = 0; INPUT input1, input2; input1.type = INPUT_KEYBOARD; input1.ki = ki1; input2.type = INPUT_KEYBOARD; input2.ki = ki2; SendInput( 1, &input1, sizeof(input1) ); //moderne Variante SendInput( 1, &input2, sizeof(input2) ); CView::OnLButtonDown(nFlags, point); } void CTastatur001View::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { if ( (::GetKeyState( VK_RCONTROL ) < 0) && (::GetKeyState( VK_LCONTROL ) < 0) ) MessageBox("Beide Strg-Tasten gedrückt.", "Sync in OnKeyDown"); if ( (::GetAsyncKeyState( VK_RCONTROL ) < 0) && (::GetAsyncKeyState( VK_LCONTROL ) < 0) ) MessageBox("Beide Strg-Tasten gedrückt.","Async in OnKeyDown"); keybd_event(VK_LCONTROL, 0, KEYEVENTF_KEYUP, 0); //alte Variante keybd_event(VK_RCONTROL, 0, KEYEVENTF_KEYUP, 0); CView::OnKeyDown(nChar, nRepCnt, nFlags); }Im vorstehenden Beispiel hat man die beiden Tastendrucke detailliert vorbereitet und einzeln verschickt. Man kann das in komprimierter Form auch wie folgt erledigen:
INPUT Input[2]; Input[0].type = INPUT_KEYBOARD; Input[0].ki.wVk = VK_LCONTROL; Input[1].type = INPUT_KEYBOARD; Input[1].ki.wVk = VK_RCONTROL; SendInput( 2, Input, sizeof(INPUT) );Probieren geht hier über studieren. Viel Spaß!

-
Ich habe das selbe Problem mit der WM_KEYDOWN Meldung in einem Dialog. Es geht einfach nicht!!
Ich habe das mit Spy++ mal probiert und festgestellt, daß bei einer Tastatureingabe immer die Meldung WM_KICKIDLE kommt. Habe keine Ahnung wieso.
Sicher ist, daß es bei einer SDI-Anwendung mit dem WM_KEYDOWN wunderbar funktioniert. Also die Meldungen zeigt Spy++ richtig an.
Bei einem Dialog aber nicht.Kann da jemand helfen???
Edit: Ich habe das jetzt doch hinbekommen. Habe jetzt die PreTranslateMessage benutzt und es hat funktioniert.
Aber da würde mich mal der Hintergrund interessieren, also warum das so ist bzw. nur so geht?
-
Ich habe jetzt schon 2 tage gesucht. Man findet leider nicht wirklich was gescheites.
Kann denn keiner was dazu sagen?Was soll WM_KEYDOWN in einem Dialog, wenn da gar nichts passiert???????
-
Nun mal ganz cool.

Bastelt mal eine Dialoganwendung, die völlig leer ist, also auch kein OK oder ESC, und fügt dort OnKey... auf WM_KEYDOWN mit einer MessageBox ein. Das funktioniert selbstverständlich, weil der Dialog selbst den Focus besitzt. Wollt ihr also dem Dialog Nachrichten schicken, dann muss er auch den Focus besitzen, ansonsten landet es bei seinen Kindern, die vielleicht nichts damit anfangen können.
-
Erhard Henkes schrieb:
Nun mal ganz cool.

Bastelt mal eine Dialoganwendung, die völlig leer ist, also auch kein OK oder ESC, und fügt dort OnKey... auf WM_KEYDOWN mit einer MessageBox ein. Das funktioniert selbstverständlich, weil der Dialog selbst den Focus besitzt. Wollt ihr also dem Dialog Nachrichten schicken, dann muss er auch den Focus besitzen, ansonsten landet es bei seinen Kindern, die vielleicht nichts damit anfangen können.
Ja genau, daß habe ich nun mittlerweile auch rausgefunden! Aber wie kann ich den nun folgendes Problem ohne PreTranslateMessage in einem modalen Dialog realisieren:?
1.leerer Dialog mit nur einem edit-feld.
2.Es geschieht eine Eingabe einer Zeichenfolge in das Edit-Feld.
3. Enter-Taste drücken und das Feld wird gelöscht und eine weitere Prozedur wird aufgerufen.
4. Danach wieder Eingabe einer Zeichenfolge usw.
5. Beenden z.B. mit ESCWie gesagt mit PreTranslateMessage funktioniert das, mir wurde aber davon abgeraten diese Funktion zu benutzen.
Danke im Vorraus!!