| Nachrichten | Application-Message-Queue | System-Message-Queue ||./img/system_message_queu.gif ||./img/keyboard_lparam.gif ||./img/keyboard_prinzip.gif | Tabelle der Nachrichten-Prefixe | Message - Cracker - Macros | Portabilität | Win32 | Win16 | Tastatur - Nachrichten | Tasten - Scan - Code | Tabelle der WM - Tastatur - Nachrichten ||./img/key1.gif | Virtual - Tasten - Code | Tabelle der Virtual - Tasten - Codes | Tasten - Zustand - Flags | Wie kann eine gedrücke SHIFT - Taste abgefragt werden? | Wie kann eine Berechnung abgebrochen werden? | Wie kann eine gedrückte CTRL - Taste
( VK_CONTROL ) per Programm simuliert werden?
| Wie kann die CAPS - LOCK - Taste
( VK_CAPITAL ) invertiert werden?
| Wie kann ein "floating-pop-up-menu"
nach der Anzeige sichtbar bleiben?
| lParam - Tastenflags | Maus - Nachrichten | WM_MOUSE - Nachrichten | Nachrichtenverfolgung

Nachrichten

Windows ist ein Nachrichten - gesteuertes System. Die Behandlung von Nachrichten ist ein zentraler Bestandteil des Betriebssystems. Es gibt eine Maus (mit zugehörigem einer System-Message-Queue). Es gibt eine tastatur (mit zugehörigem einer System-Message-Queue). Zu jeder Applikation wird eine Applikation-Message-Queue eingerichtet, in die jene Nachrichten (standardisiert) gestellt werden, die zu dieser Applikation gehören. Beispiel für den Nachrichten-Fluß bei einer Tasten-Nachricht:

KEY-
BOARD
(Ereignis)
pfeil_r.gif Keyboard-Device-Driver
holt Scan-Code
vom Keyboard und
stellt diesen in
pfeil_r.gif SYSTEM-
MESSAGE-
QUEUE
(geräte-
spezifisch)
pfeil_r.gif Windows bringt
die geräte-
spezifische
Nachricht auf
standardisierte
MSG-Form (WM_...),
stellt diese in
pfeil_r.gif APPLICATION-
MESSAGE-
QUEUE
(Thread-Message-Queue)
(MSG: WM_...)
pfeil_ro.gif pfeil_r.gif pfeil_ru.gif
pfeil_o.gif APPLICATION-
NACHRICHTEN-
Schleife
pfeil_u.gif
pfeil_lo.gif pfeil_l.gif pfeil_lu.gif
Dispatch
Message

(&msg)

ruft die
CALLBACK-
Funktion auf,
die zu
msg.hWin
gehört
System-Message-Queue

Die Maus- und Tastatur - Ereignisse werden der System - Message - Queue( Hardware - Buffer ) entnommen und in ein einheitliches MSG - Format gebracht. Dann werden die Nachrichten in die Application - Message - Queue( Nachrichten an unser Fenster ) gestellt. Durch GetMessage() werden die Nachrichten dem Application - Buffer entnommen. Sollen z.B. sämtliche Nachrichten an alle Anwendungen abgehorcht werden, so ist direkt hinter der System - Message - Queue ( Hardware - Buffer ) ein Hook ( z.B. CallNextHookEx ) einzubauen.

Application-Message-Queue

Jede Anwendung hat eine eigene Application - Message - Queue. Ist diese leer, so können in der Zwischenzeit andere Programme ausgeführt werden ( kooperatives Multitasking ).

Für die Nachrichten verwendet Windows den MSG-Typ:

 
typedef struct tagMSG {
  HWND hwnd ;
  UINT uMsg ;
  WPARAM wParam ;
  LPARAM lParam ;
  DWORD time ;
  POINT pt ;
} MSG ;
  MSG msg ;
system_message_queu.gif
Keyboard-Prinzip:
keyboard_prinzip.gif
Beispiel lParam bei uMsg = WM_KEYDOWN:
WM_KEYDOWN: lParam :
keyboard_lparam.gif
Tabelle der Nachrichten-Prefixe
Prefix Message category
ABM Application desktop toolbar
BM Button control
CB Combo box control
CBEMExtended combo box control
CDM Common dialog box
DBT Device
DL Drag list box
DM Default push button control
DTM Date and time picker control
EM Edit control
Prefix Message category
HDM Header control
HKM Hot key control
IPM IP address control
LB List box control
LVM List view control
MCM Month calendar control
ABM Application desktop toolbar
PBM Progress bar
PGM Pager control
PSM Property sheet
RB Rebar control
Prefix Message category
SB Status bar window
ABM Application desktop toolbar
SBM Scroll bar control
STM Static control
TB Toolbar
TBM Trackbar
TCM Tab control
TTM Tooltip control
TVM Tree-view control
UDM Up-down control
WM General window

Die msg-Nachricht enthält auch msg.message. Durch message wird die Art der Nachricht eindeutig spezifiziert.

Jede Nachricht kann in msg.wParam, msg.lParam zusätzliche Informationen hinterlegen. Die genaue Bedeutung der msg.wParam, msg.lParam - Bits hängt von msg.message ab. Für viele einfache Nachrichten reichen die Bits von wParam, lParam aus. In msg.lParam kann auch ein Zeiger auf benötigte Informationen enthalten sein.

msg.time enthält die Ereignis - Zeit.

Die Cursor - Koordinaten ( zur Ereignis-Zeit ) werden in pt übergeben.

Soll z.B. die nächste Nachricht aus der Application-Message-Queue geholt werden, so wird mit GetMessage( & msg ) diese Nachricht in die ( vorher angelegte ) Variable

  MSG msg ;

gespeichert. Haupt - Nachrichten - Schleife ( nach WinMain ) ist eine ( beinahe ) Unendlich-Schleife, die nur durch die WM_QUIT - Nachricht verlassen werden kann.

  MSG msg;
  while ( GetMessage( & msg, NULL, 0, 0 ) ) {
    ...
    DispatchMessage ( & msg ) ;
  }

Durch RegisterClass() wurde unsere ( Klassen - ) CALLBACK - Funktion WNDCLASS. lpfnWndProc = ( WNDPROC ) WndProc ; eingetragen. Durch DispatchMessage ( & msg ) wird die aktuelle Nachricht an diese Funktion weiter gereicht, d.h. die WndProc() wird mit den msg - Paramtern aufgerufen. Jedes Fenster hat ein eindeutiges Handle. Deshalb kann DispatchMessage ( & msg ) mit Hilfe von msg.hWnd die Nachricht über die ( globalen ) Klassen - Daten - Struktur an die CALLBACK - Funktion weiter geben. Die Haupt - Nachrichten - Schleife wird bei einer WM_QUIT - Botschaft abgebrochen. Nach dem Eintreffen von WM_DESTROY fügen wir in der Application - Message - Queue durch PostQuitMessage() die Nachricht WM_QUIT ein. Wenn GetMessage() die WM_QUIT - Nachricht entnimmt, so wird die Haupt - Nachrichten - Schleife beendet.




Message - Cracker - Macros

In windowsX.h sind die Message - Cracker - Macros enthalten. Durch diese Macros wird das Nachrichten - Carsting vermieden und die Win16/Win32 - Portabilität erhöht.

Beispiele für soche Macros sind:

#define HANDLE_MSG(hWnd, uMsg, fn)    \
    case (uMsg): return HANDLE_##uMsg((hWnd), (wParam), (lParam), (fn))

Ein HANDLE_MSG(hWnd, WM_CHAR, fn) - Macro - Aufruf enpandiert in ein weiteres ( in WindowsX.h enthaltenes ) HANDLE_WM_CHAR() - Macro:

/* void Cls_OnChar( HWND hWnd, TCHAR ch, int cRepeat) */
#define HANDLE_WM_CHAR( hWnd, wParam, lParam, fn) \
    ((fn)(( hWnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
#define FORWARD_WM_CHAR( hWnd, ch, cRepeat, fn) \
    (void)(fn)(( hWnd), WM_CHAR, (WPARAM)(TCHAR)(ch), MAKELPARAM((cRepeat),0))

Viele HANDLE_ - Macros ( z.B. HANDLE_WM_CHAR ) zerlegen die Nachrichten typentreu in die enthaltenen Nachrichten - Parameter und rufen damit die Funktion ( hier fn ) auf. Die FORWARD_ - Macros machen diesen Vorgang rückgängig und erzeugen wieder die ursprüngliche ( hWnd, uMsg, wParam, lParam ) - Nachricht.

Mit dem HANDLE_MSG - Macro hat eine CALLBACK - Funktion etwa den folgenden Aufbau:

LRESULT CALLBACK myCallbackProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
   switch (uMsg) {
     HANDLE_MSG( hWnd, WM_CHAR, myOnChar);
     HANDLE_MSG( ... );
     ...
     default:
       return myOrgProc( hWnd, uMsg, wParam, lParam);
   }
 }

Vielfach wird myOrgProc() der DefWindowProc() von Windows entsprechen. Die Funktion myOnChar() wird etwa wie folgt aufgebaut:

  void myOnChar( HWND hWnd, UINT ch, int cRepeat) {
   if ( ch == testvalue ) {  // handle it here
   } else {
     FORWARD_WM_CHAR( hWnd, ch, cRepeat, myOrgProc);
   }
 }

Das FORWARD_ - Macro setzt die zerlegte Nachricht wieder zur ursprünglichen ( hWnd, uMsg, wParam, lParam ) - Nachricht zusammen.

In WindowsX.h sind ( als Kommentar ) die Funktions - Parameter für myOnChar() angegeben.

  /* void Cls_OnChar( HWND hWnd, TCHAR ch, int cRepeat) */


Soll für myOrgProc() nicht DefWindowProc() sondern die orginal Klassen - CALLBACK - Funktion lpOrgProc aufgerufen werden, so kann myOrgProc definiert werden durch:

 LRESULT myOrgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
   return CallWindowProc( lpOrgProc, hWnd, uMsg, wParam, lParam);
}

CallWindowProc() ruft die Funktion lpOrgProc auf. In WindowsX.h ist

 #define Sumy_classWindow( hWnd, lpfn)             \
   ((WNDPROC)SetWindowLong(( hWnd), GWL_WNDPROC, \
   (LPARAM)(WNDPROC)MakeProcInstance((FARPROC)(lpfn),GetModuleHandle(NULL))))

enthalten. Damit kann ein Sumy_classing gemäß erfolgen durch

 WNDPROC lpOrgProc = NULL;
lpOrgProc = Sumy_classWindow( hWnd, myCallbackProc));
 ...
Portabilität

Beim Übergang von WIN16 auf WIN32 hat sich das Nachrichten - Format geändert. Bei WIN16 war wParam ein WORD - Typ, bei WIN32 ist wParam ein LONG - Typ. Bei WIN32 benötigt ein Handle 32 Bits.

Win32

             32 Bit                            32 Bit
 |---------------------------------|---------------------------------|
 |              wParam             |              lParam             |
 |----------------|----------------|---------------------------------|
 |                |                |                                 |
 |      wmCMD     |      wmID      |              wmHWND             |

Win16

                     16 Bit                    32 Bit
                  |----------------|---------------------------------|
                  |     wParam     |              lParam             |
                  |----------------|---------------------------------|
                  |                |                |                |
                  |      wmID      |      wmCMD     |    wmHWND      |

Der Button sendet Nachrichten an das Parent - Window, wenn sich sein Zustand ändert. Der Button - Identifizierer wird mit wmID, die Notification Messages ( z.B. BN_CLICKED ) wird mit wmCMD und das Control - Handle mit wmHWND bezeichnet. Wegen der WIN16/WIN32 - Portabilität sind die windoesX.h - Macros sinnvoll:

Nachrichten - Casting WindosX.h-Macros
 #ifdef WIN16 
   int  wmCMD = HIWORD( lParam ) ;
 #else
   int  wmCMD = HIWORD( wParam ) ;
 #endif 
 int  wmID  =  LOWORD( wParam );
 HWND wmHWND= (HWND)(UINT)lParam;
int  wmCMD = GET_WM_COMMAND_CMD (wParam,lParam);
 int  wmID  = GET_WM_COMMAND_ID  (wParam,lParam);
 HWND wmHWND= GET_WM_COMMAND_HWND(wParam,lParam);



Tastatur - Nachrichten

Wenn Prozessen mit unterschiedlicher Ausführungs - Geschwindigkeit synchronisiert werden müssen, so werden Warteschlangen ( Buffer ) benötigt. Der schreibende Prozeß hinterlegt die unregelmäßig eintreffenden Ereignisse in einem Buffer. Der lesende Prozeß holt sich die Nachrichten und verarbeitet diese mit der eigenen Geschwindigkeit. Bei dem Windows - System - Tastatur - Buffer handelt es sich um einen Ring - Buffer. Bei DOS enthält der Tastatur - Buffer 32 Worte ( 64 Byte ).

#include <stdio.h>
extern key_read( int*, int* ); 
void main( ) { 
   int *ascii_ptr, *scan_ptr, num, num1; num = 0; num1 = 0; 
   ascii_ptr = &num; scan_ptr = &num1; // initialize pointers to zero
   key_read( ascii_ptr, scan_ptr );  // call assembly routine
// print the high byte - ASCII code, and the low byte - extended code
// of the character placed in the keyboard buffer
   printf( "ASCII Code hex %x or decimal %d\n", *ascii_ptr,"           " *ascii_ptr);
   printf( "EXTENDED Code hex %x "           "or decimal %d\n", *scan_ptr, *scan_ptr);
}
****************************************************** 
         .model small,c
         .data
         .code
PUBLIC   key_read
key_read PROC 
         PUSH  bp          ;save the base pointer
         MOV   bp, sp
; Invoke Int 21h Function Ch to clear the keyboard buffer before ; 
; accepting a keystroke. 
         MOV   ah, 0CH
         MOV   al, 0
         INT   21h
; Invoke Int 16h Function 0h to place the character code in the AX ; register. 
         MOV   ah, 0H
         INT   16H
         MOV   bx, [bp+4]  ;ASCII returned
         MOV   [bx], al
         MOV   bx, [bp+6]  ;Extended code returned
         MOV   [bx], ah
         POP   bp
         RET
key_read ENDP 
         END
Tasten - Scan - Code

Der folgende Tasten - Scan - Code ist festgelegt worden:

key1.gif: Tastatur-Code ...
key2.gif: Tastatur-Code ...

Bei Windows kann der System - Tastatur - Buffer 60 Zeichen aufnehmen.

Im System - Nachrichten - Buffer kommt der Tasten - Scan - Code. Die Tasten - Nachricht wird aufbereitet ( einheitliches MSG - Format ) und in den Applikations - Nachrichten - Buffer gestellt.

Bei Windows hat ein Fenster den Focus. Dieses Fenster ist i.a. an der blauen Titel - Zeile erkennbar. Die Tasten - Nachrichten werden in der Haupt - Nachrichten - Schleife aus dem Applikations - Buffer geholt ( GetMessage ) und die CALLBACK - Funktion des Fensters wird mit der Tasten - Nachricht ( uMsg, wParam, lParam ) aufgerufen, d.h.

Vor dem Aufruf der Fensters - CALLBACK - Funktion werden durch das Windows - System die Parameter der Nachricht auf den Stack gelegt, d.h. die Nachricht wird über den Aufrufer - Stack an die CALLBACK - Funktion übergeben. An hWnd ist das Fenster ( genauer: der Speicher für die Fensterdaten ) erreichbar. In uMsg ist die Tasten - Nachricht eindeutig identifizierbar. Mit wParam, lParam können die Tasten unterschieden werden.

LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { 
  switch ( uMsg ) {
    ...
    case WM_CHAR : {
      switch ( LOWORD(wParam) ) {
        case ‘\b’ : Rücktaste; break;
        case ‘\t’ : Tabulatortaste; break;
        case ‘\n’ : LF-Taste; break;
        default : //normales Zeichen bearbeiten
      }
    }
    ...
  }
  return DefWindowProc( hWnd, uMsg, wParam, lParam ) ;
}

In dem obigen Beispiel werden nur einige WM_CHAR - Nachrichten behandelt.

Tabelle der WM - Tastatur - Nachrichten

Es gibt die folgenden Keyboard - Nachrichten:

Tasten-
Nachricht
wParam, lParam enthalten ...
WM_ACTIVATE fActive = LOWORD(wParam); // activation flag
fMinimized = (BOOL) HIWORD(wParam); // minimized flag
hwndPrevious = (HWND) lParam; // window handle
WM_SETFOCUS // handle of window losing focus
hwndLoseFocus = (HWND) wParam;
WM_KILLFOCUS // handle of window receiving focus
hwndGetFocus = (HWND) wParam;
WM_KEYDOWN
WM_KEYUP
nVirtKey = (int) wParam; // virtual-key code
lKeyData = lParam; // key data
WM_CHAR
WM_SYSCHAR
chCharCode = (TCHAR) wParam; // character code
lKeyData = lParam; // key data
WM_HOTKEY idHotKey = (int) wParam; // identifier of hot key
fuModifiers = (UINT) LOWORD(lParam); // key-modifier flags
uVirtKey = (UINT) HIWORD(lParam); // virtual-key code
Tasten-
Nachricht
wParam, lParam enthalten ...
WM_GETHOTKEY wParam = 0; // not used; must be zero
lParam = 0; // not used; must be zero
WM_SETHOTKEY // virtual-key code and modifiers of hot key
wParam = (WPARAM) MAKEWORD(vkey, modifiers)
lParam = 0; // not used; must be zero
WM_DEADCHAR chCharCode = (TCHAR) wParam; // character code
lKeyData = lParam; // key data
WM_SYSDEADCHAR chCharCode = (TCHAR) wParam; // character code
lKeyData = lParam; // key data
WM_SYSKEYDOWN
WM_SYSKEYUP
nVirtKey = (int) wParam; // virtual-key code
lKeyData = lParam; // key data
Virtual - Tasten - Code

Je Taste werden mehr als eine Nachricht generiert. Wird z.B. der Klein - Buchstabe a auf der Tastatur gedrückt, so wird die CALLBACK - Funktion mehrfach aufgerufen, d.h. es werden die folgenden 3 Nachrichten generiert:

Wird z.B. der Groß - Buchstabe A auf der Tastatur gedrückt, so wird die CALLBACK - Funktion mehrfach aufgerufen, d.h. es werden die folgenden 5 Nachrichten generiert:

Die (V)irtuellen (K)ey - Nachrichten ( VK_... ) können in der CALLBACK WndProc - Funktion etwa wie folgt benutzt werden.

LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { 
  switch ( uMsg ) {
    ...
    case WM_KEYDOWN : {
      switch ( wParam ) {
        ...
        case VK_LEFT: 
             //die Caret-nach-links-Taste ( Pfeilchen-Taste ) 
             //wurde gedrückt, jetzt soll ...
        break ; 
        case VK_F1: ...
      } ...
    } ...
  } ...
}

Die RETURN - Tasten können unterschieden werden durch das 24. Bit von lParam.

  switch ( uMsg ) {
    case WM_KEYDOWN : {
      switch ( LOWORD(wParam) ) {
        ...
        case VK_RETURN:
          if ( lParam & 0x01000000L ) { // Enter-Taste ( numeric  keypad )
          } else {                      // Enter-Taste ( standard keypad )
          } break ; 
        } ...
      } ...
    } ...
  } 
Tabelle der Virtual - Tasten - Codes

Bei einer WM_KEYDOWN - Nachricht wird in LOWORD(wParam) der ( standardisierte ) Virtual - Key - Code übergeben. Die folgende Tabelle enthält die #define`s.

VK_LBUTTON = 0x01 VK_RBUTTON = 0x02 VK_CANCEL = 0x03 VK_MBUTTON = 0x04 VK_BACK = 0x08 VK_TAB = 0x09
VK_CLEAR = 0x0C VK_RETURN = 0x0D VK_SHIFT = 0x10 VK_CONTROL = 0x11 VK_MENU = 0x12 VK_PAUSE = 0x13
VK_CAPITAL = 0x14 VK_ESCAPE = 0x1B VK_SPACE = 0x20 VK_PRIOR = 0x21 VK_NEXT = 0x22 VK_END = 0x23
VK_HOME = 0x24 VK_LEFT = 0x25 VK_UP = 0x26 VK_RIGHT = 0x27 VK_DOWN = 0x28 VK_SELECT = 0x29
VK_PRINT = 0x2A VK_EXECUTE = 0x2B VK_SNAPSHOT = 0x2C VK_INSERT = 0x2D VK_DELETE = 0x2E VK_HELP = 0x2F
VK_0
..VK_9
= 0x30
...0x39
VK_A
..VK_Z
= 0x41
..0x5A
VK_LWIN = 0x5B VK_RWIN = 0x5C VK_APPS = 0x5D VK_NUMPAD0 = 0x60
VK_NUMPAD1 = 0x61 VK_NUMPAD2 = 0x62 VK_NUMPAD3 = 0x63 VK_NUMPAD4 = 0x64 VK_NUMPAD5 = 0x65 VK_NUMPAD6 = 0x66
VK_NUMPAD7 = 0x67 VK_NUMPAD8 = 0x68 VK_NUMPAD9 = 0x69 VK_MULTIPLY = 0x6A VK_ADD = 0x6B VK_SEPARATOR = 0x6C
VK_SUBTRACT = 0x6D VK_DECIMAL = 0x6E VK_DIVIDE = 0x6F VK_F1 = 0x70 VK_F2 = 0x71 VK_F3 = 0x72
VK_F4 = 0x73 VK_F5 = 0x74 VK_F6 = 0x75 VK_F7 = 0x76 VK_F8 = 0x77 VK_F9 = 0x78
VK_F10 = 0x79 VK_F11 = 0x7A VK_F12 = 0x7B VK_F13 = 0x7C VK_F14 = 0x7D VK_F15 = 0x7E
VK_F16 = 0x7F VK_F17 = 0x80 VK_F18 = 0x81 VK_F19 = 0x82 VK_F20 = 0x83 VK_F21 = 0x84
VK_F22 = 0x85 VK_F23 = 0x86 VK_F24 = 0x87 VK_NUMLOCK = 0x90 VK_SCROLL = 0x91 VK_LSHIFT = 0xA0
VK_RSHIFT = 0xA1 VK_LCONTROL = 0xA2 VK_RCONTROL = 0xA3 VK_LMENU = 0xA4 VK_RMENU = 0xA5 VK_PROCESSKEY
WINVER<=0x0400
= 0xE5
VK_ATTN = 0xF6 VK_CRSEL = 0xF7 VK_EXSEL = 0xF8 VK_EREOF = 0xF9 VK_PLAY = 0xFA VK_ZOOM = 0xFB
VK_NONAME = 0xFC VK_PA1 = 0xFD VK_OEM_CLEAR = 0xFE            

Z.B. wird durch die Alt - Taste die wParam = VK_MENU - Nachricht generiert.

VK_L... bzw. VK_R... gehören zu der linken bzw. rechten Alt - , Ctrl - und Shift - Tasten und werden nur als Parameter bei den GetAsyncKeyState(), GetKeyState() - Funktionen benutzt.

Tasten - Zustand - Flags

Der aktuelle Zustand aller Tasten wird in einem Buffer protokolliert. Auch die Applikation kann diese Einträge lesen/schreiben, wobei zwischen synchron/asynchron unterschieden wird. Wird eine neue Tasten - Nachricht in den Applikations - Nachrichten - Warteschlange ( Applikations - Buffer ) eingetragen, so ändert sich der asynchron Keyboard - Zustand. Der synchron Keyboard - Zustand entspricht dem momentanen Zustand der Tastatur.

Wie kann eine gedrücke SHIFT - Taste abgefragt werden?
  if ( GetKeyState ( VK_SHIFT ) & 0x80000000L ) { 
    // SHIFT - Taste gedrückt
  }

GetKeyState() ( imGegensatz zu GetAsyncKeyState ) liefert den Zustand ohne Verzögerung zurück. Durch GetKeyState ( VK_LBUTTON ) wird z.B. der aktuelle Zustand der linken Maustaste abgefragt.

Wie kann eine Berechnung abgebrochen werden?

Dauert z.B. eine Berechnung zu lange und soll diese Berechnung durch

abgebrochen werden, so kann dies mit der ( asynchronen ) GetAsyncKeyState() - Funktion

  if ( ( GetAsyncKeyState ( VK_LBUTTON ) < 0 ) // linke Maus - Taste down
    || ( GetAsyncKeyState ( VK_ESCAPE  ) < 0 )  // Escape Taste
     ) { 
         // ... z.B. exit ( -1 ); oder 
         // ... SendMessage ( hWnd, WM_CLOSE, 0, 0 )
    }

erfolgen. Um den Zustand von mehreren Tasten zu untersuchen, wie z.B. die

können alle 256 Virtual Keys in einen Buffer buf    kopiert werden. Die Funktionen GetKeyboardState(), SetKeyboardState(), GetAsyncKeyState(), GetKeyState(), MapVirtualKey() werden benutzt. Es kann dann z.B. zwischen linken und rechten SHIFT - Tasten unterschieden werden. Die GetKeyboardState( buf ) - Funktion kopiert alle 256 Virtual Keys in einen Buffer buf. Eine Taste ist gerade gedrückt, wenn das 7. Bit gesetzt ist ( sonst 0 ). Der Gedrückt/Losgelassen - Zustand hat gewechselt, wenn das 0. Bit gleich 1 ist ( tggled ). Normalerweise ist eine Taste nicht gedrückt, dann ist das 0. Bit gleich 1.

Wie kann eine gedrückte CTRL - Taste
( VK_CONTROL ) per Programm simuliert werden?
 BYTE buf[256];
 GetKeyboardState( buf ); // hole VK_ - Keys
 buf[VK_CONTROL] |= 0x80;   // setze "gedrückt" bei VK_CONTROL - Taste
 SetKeyboardState( buf ); // schreibe VK_ - Keys zurück
Wie kann die CAPS - LOCK - Taste
( VK_CAPITAL ) invertiert werden?
 BYTE buf[256];
 GetKeyboardState( buf ); 
 if ( buf[VK_CAPITAL] & 1 ) buf[VK_CAPITAL] &= 0xFE;
 else                       buf[VK_CAPITAL] |= 0x01;
SetKeyboardState( buf );

VK_L... bzw. VK_R... gehören zu der linken bzw. rechten Alt - , Ctrl - und Shift - Tasten und werden nur als Parameter bei den GetAsyncKeyState(), GetKeyState() - Funktionen benutzt.

Wie kann ein "floating-pop-up-menu"
nach der Anzeige sichtbar bleiben?
case WM_RBUTTONDOWN: {
  BYTE buf[256];
  GetKeyboardState( buf ); // hole VK_ - Keys
  buf[VK_RBUTTON] = 0x00;  // setze "losgelassen" bei VK_RBUTTON - Maus - Taste
  SetKeyboardState( buf ); // schreibe VK_ - Keys zurück
  ... create - pop - up - Menu ...
  ... call TrackPopUp          ...
  break; }
lParam - Tastenflags

TranslateMessage() benutzt die Funktion ToAsciiEx() um den virtuellen Key - Code ( z.B. für die WM_KEYDOWN - Nachricht ) zu erzeugen. Eine Nachricht enthält in LOWORD( lParam ) die Anzahl von automatischen Tasten - Wiederholungen. Durch diesen Anschlag - Wiederholungs - Zähler werden Überflutungen von Nachrichten vermieden.

In HIWORD( lParam ) sind interessante Tasten - Flags, die im folgenden erklärt werden. Eine Nachricht enthält in HIWORD( lParam ) interessante Tasten - Flags, die im folgenden erklärt werden.

keyboard_lparam.gif
Bit HIWORD( lParam ) - Bit Bit HIWORD( lParam ) - Bit Bit HIWORD( lParam ) - Bit Bit HIWORD( lParam ) - Bit
15 1 if the key is up;
0 if the key is down.
14 1 if the key was previously up;
0 if the key was previously down.
13 1 if the ALT key is down. 12 1 if Windows displays a menu.
11 1 if Windows displays a dialog box. 10 Not used. 9 Not used. 8 1 if the key is extended;
0 if it is not.
7 generally 0. 6..0 hardware-scan code
( used mainly in the translation of ALT+number-pad character code input )



Maus - Nachrichten

Die Maus - Nachrichten werden in der Haupt - Nachrichten - Schleife aus dem Applikations - Buffer geholt ( GetMessage ) und die CALLBACK - Funktion des Fensters wird mit der Maus - Nachricht ( uMsg, wParam, lParam ) aufgerufen, d.h.

Vor dem Aufruf der Fensters - CALLBACK - Funktion werden durch das Windows - System die Parameter der Nachricht auf den Stack gelegt, d.h. die Nachricht wird über den Aufrufer - Stack an die CALLBACK - Funktion übergeben. An hWnd ist das Fenster ( genauer: der Speicher für die Fensterdaten ) erreichbar. In uMsg ist die Maus - Nachricht eindeutig identifizierbar. Typische uMsg sind: WM_LBUTTONDOWN, WM_RBOTTOMDOWN, WM_MOUSEMOVE, WM_NCLBUTTONDBLCLICK.

Die Behandlung von Maus - Ereignisses geschieht vielfach in der CALLBACK - Funktion. In lParam wird die aktuelle Maus - Position an die CALLBACK - Funktion übergeben.

LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { 
  static int xPos,yPos;
  switch ( uMsg ) {
    ...
    case WM_LBUTTONDOWN :
         xPos = (int) LOWORD(lparam); // 16 BIT 
         yPos = (int) HIWORD(lparam); // pt = MAKEPOINT(lparam); // 32 BIT, pt vom Typ Point 
    break; 
    ...
  }
  ...
  return DefWindowProc( hWnd, uMsg, wParam, lParam ) ;
}

Für einen Doppel - Klick muß nach WM_LBUTTONDOWN ein Zähler gestartet werden. Deshalb ist für einen Doppelklick ein Eintrag

  WNDCLASSEX wc={ 0 }; 
  wc.cbSize=sizeof(WNDCLASSEX);
  ...
  wc.style = ... | CS_DBLCLKS;  // Zeitgeber ½ sec. 
RegisterClass( & wc ); 
erforderlich.
WM_MOUSE - Nachrichten

Ein Fenster besteht aus dem Fenster - Inneren ( Client - Bereich ) und dem Nicht Client - Bereich ( NC ). Auch der Fenster - Rand gehört zu NC. Maus-Nachricht

Nachricht wParam, lParam enthalten ...
WM_CAPTURECHANGED hwndNewCapture = (HWND) lParam; // handle of window to gain mouse capture
WM_MOUSEMOVE
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_RBUTTONDOWN
WM_RBUTTONUP
WM_MBUTTONDOWN
WM_MBUTTONUP
WM_LBUTTONDBLCLK
WM_MBUTTONDBLCLK
WM_RBUTTONDBLCLK
fwKeys = wParam; // key flags
xPos = LOWORD(lParam); // horizontal position of cursor
yPos = HIWORD(lParam); // vertical position of cursor
fwKeys:
MK_CONTROL Set if the CTRL key is down.
MK_LBUTTON Set if the left mouse button is down.
MK_MBUTTON Set if the middle mouse button is down.
MK_RBUTTON Set if the right mouse button is down.
MK_SHIFT Set if the SHIFT key is down.
WM_MOUSEACTIVATE hwndTopLevel = (HWND) wParam; // handle of top-level parent
nHittest = (INT) LOWORD(lParam); // hit-test value
uMsg = (UINT) HIWORD(lParam); // mouse message
nHittest is the return value of DefWindowProc:
MA_ACTIVATE Activates the window, and does not discard the mouse message.
MA_ACTIVATEANDEAT Activates the window, and discards the mouse message.
MA_NOACTIVATE Does not activate the window, and does not discard the mouse message.
MA_NOACTIVATEANDEAT Does not activate the window, but discards the mouse message
WM_MOUSEWHEEL fwKeys = LOWORD(wParam); // key flags
zDelta = (short) HIWORD(wParam); // wheel rotation
xPos = (short) LOWORD(lParam); // horizontal position of pointer
yPos = (short) HIWORD(lParam); // vertical position of pointer
fwKeys:
MK_CONTROL Set if the CTRL key is down.
MK_LBUTTON Set if the left mouse button is down.
MK_MBUTTON Set if the middle mouse button is down.
MK_RBUTTON Set if the right mouse button is down.
MK_SHIFT Set if the SHIFT key is down.
WM_NCHITTEST xPos = LOWORD(lParam); // horizontal screen position of cursor
yPos = HIWORD(lParam); // vertical screen position of cursor
Value Location of hot spot is the return value of DefWindowProc:
HTBORDER In the border of a window that does not have a sizing border
HTBOTTOM In the lower horizontal border of a window
HTBOTTOMLEFT In the lower-left corner of a window border
HTBOTTOMRIGHT In the lower-right corner of a window border
HTCAPTION In a title bar
HTCLIENT In a client area
HTCLOSE In a close button HTERROR On the screen background or on a dividing line between windows
    (same as HTNOWHERE, except that the DefWindowProc
    function produces a system beep to indicate an error)
HTGROWBOX In a size box (same as HTSIZE) HTHELP In a Help button
HTHSCROLL In a horizontal scroll bar
HTLEFT In the left border of a window
HTMENU In a menu
HTMAXBUTTON In Maximize button
HTMINBUTTON In Minimize button
HTNOWHERE On the screen background or on a dividing line between windows
HTREDUCE In a Minimize button
HTRIGHT In the right border of a window
HTSIZE In a size box (same as HTGROWBOX)
HTSYSMENU In a System menu or in a Close button in a child window
HTTOP In the upper horizontal border of a window
HTTOPLEFT In the upper-left corner of a window border
HTTOPRIGHT In the upper right corner of a window border
HTTRANSPARENT In a window currently covered by another window
HTVSCROLL In the vertical scroll bar
HTZOOM In a Maximize button
WM_NCLBUTTONDBLCLK
WM_NCLBUTTONDBLCLK
WM_NCLBUTTONDOWN
WM_NCLBUTTONUP
WM_NCMBUTTONDBLCLK
WM_NCMBUTTONDOWN
WM_NCMBUTTONUP
WM_NCMOUSEMOVE
WM_NCRBUTTONDBLCLK
WM_NCRBUTTONDOWN
WM_NCRBUTTONUP
nHittest = (INT) wParam; // hit-test value
pts = MAKEPOINTS(lParam); // position of cursor
nHittest see WM_NCHITTEST

Das Maus - Rad ( WM_MOUSEWHEEL ) kann z.B. zur Steuerung von Scroll - Bars eingesetzt werden.

Nachrichtenverfolgung

Um die Folge von Nachrichten zu untersuchen, soll die folgende Funktion schreibe_nachrichten(UINT uMsg,WPARAM wParam,LPARAM lParam) die Folge der Nachrichten anzeigen. Der Header-File enthält die #define's für die Nachrichten, die in einen Array der Art { "WM_DESTROY", WM_DESTROY}// 0x0002 hinterlegt werden. Steht der Aufruf schreibe_nachrichten(uMsg,wParam,lParam) in einer CALLBACK-Funktion, so durchläuft for(int i = 0;i < n; i++) {...} alle Array-Einträgt und gibt den gefundenen aus.

Das stdout_to()-Macro ermöglicht (mit Hilfe der freopen()-Funktion), die Standard-Ausgabe der printf's (einer Console-Applikation) in den aktuellen Quelltext-File (__FILE__) umzuleiten. Die Benutzung von stdout_to erfolgt etwa gemäss:

stdout_to(__FILE__);
printf("\n // ...");
printf("\n // ...");
stdout_to("CON");

Die folgende Funktion schreibe_nachrichten(UINT uMsg,WPARAM wParam,LPARAM lParam) wird in der CALLBACK-Funktion aufgerufen. In schreibe_nachrichten() wird beijeder ankommenden Nachricht die Nachrichten-Tabelle msgdesc[] durchlaufen und bei Übereinstimmung wird der Name der aktuellen uMsg-Nachricht (und auch wParam,lParam) ausgegeben.

void schreibe_nachrichten(UINT uMsg,WPARAM wParam,LPARAM lParam) 
{ 
  #undef stdout_to
  #define stdout_to(f) if((freopen(f,"at", stdout))==NULL)\
                 {fprintf(stderr,"\nredir-err\n");exit(-1);}

  typedef struct { LPTSTR psz; UINT uMsg;} MSGDESC; 
  
  static MSGDESC msgdesc[] = 
  { 
    { "WM_NULL",     WM_NULL},         // 0x0000 
    { "WM_CREATE",   WM_CREATE},       // 0x0001 
    { "WM_DESTROY",  WM_DESTROY},      // 0x0002 
    { "WM_MOVE",     WM_MOVE},         // 0x0003 
    { "WM_SIZE",     WM_SIZE},         // 0x0005 
    { "WM_ACTIVATE", WM_ACTIVATE},     // 0x0006 
    { "WM_SETFOCUS", WM_SETFOCUS},     // 0x0007 
    ...
  };
  static UINT j; if(j == 0) stdout_to(__FILE__); 
  UINT len = sizeof(msgdesc)/sizeof(msgdesc[0]);

  for(int i = 0; i < len; i++) {
    if(uMsg == msgdesc[i].uMsg) { 
      printf("\n// %03u %04x %s", j++, i, msgdesc[i].psz);
      if(uMsg == WM_COMMAND)
        printf(" wPar=0x%08x lPar=0x%08x",wParam,lParam);
    }
  } 
}

Es gibt Werkzeuge, die eine fortgeschrittene Verfolgung der Nachrichten erlauben (z.B. spy++).