Programmieren mit C++

Visual C++

Systemnahe Lösungen mit VC++

Numlock

Wie kann der Status der NumLock-Taste unter Windows NT und Win 9x gesetzt und abgefragt werden?

Frage

Die Kontrolle der NumLock-Taste ist nicht so einfach, wie es auf den ersten Blick aussieht, zumal Windows 9x und NT ein unterschiedliches Handling für das Umschalten der Taste erfordern. Das Handling der NumLock-Taste ist tief im BIOS eingebettet: Ist die Taste gedrückt, erzeugen alle Tasten des numerischen Blocks Ziffern, während sie ansonsten Bewegungscodes liefern. Für eine Anwendung besteht keine Möglichkeit herauszufinden, ob es sich um eine Taste des numerischen Blocks handelt oder eine andere Cursortaste.

Als naheliegend bietet es sich an, für die NumLock-Taste einen Accelerator zu definieren. Dies schlägt in der Praxis jedoch fehl, da die Taste im gedrückten Zustand wiederholt Accelerator-Events erzeugt, während der NumLock-Status jedoch unverändert bleibt, bis die Taste wieder losgelassen wird. Hinzu kommt eine Abweichung zwischen BIOS und Windows: Im BIOS ändert sich der NumLock-Status, wenn die NumLock-Taste gedrückt wird, in Windows 9x/NT wird der Status erst beim Loslassen der Taste geändert.

Der aktuelle Zustand der NumLock-Taste kann über GetKeyState() ermittelt werden. Allerdings ist der Status der NumLock-Taste nicht immer auf dem aktuellen Stand. Wird die Taste außerhalb der eigenen Anwendung gedrückt, ist der korrekte Zustand nicht verfügbar, solange die eigene Anwendung aktiv ist (WM_ACTIVATE erhalten). Erst wenn die Anwendung in den Idle-Loop (CWinApp::OnIdle) geht, kann der korrekte NumLock-Status wieder ermittelt werden.

Die Lösung für die aufgezeigte Problematik ist nachfolgend im Rahmen der Klasse CNumLock aufgezeigt. Die Klasse beherbergt das private Datenmember

Lösung

OSVERSIONINFO m_OSVersionInfo;

Die Struktur speichert die Information, ob als System Windows 9x oder NT läuft. Initialisiert wird das Member im Konstruktor durch die API-Funktion GetVersionEx(), verwendet in der Methode CNumLock::ToggleNumLock().

Datenmember

int m_iReqdNumLockState;

Das Datenmember m_iReqdNumLockStatewird ist ebenfalls im Konstruktor definiert und enthält entweder den Wert „0“ oder „1“, abhängig vom gewünschten NumLock-Status der Anwendung. Der Wert ist so gewählt worden, damit ein einfacher Vergleich mit dem aktuellen Status unter Verwendung von GetKeyState() möglich ist.

Member m_iReqd-NumLockState

bool m_bIgnoreKeyInput; 

m_bIgnoreKeyInput wird benötigt, um unerwünschte Tastatureingaben auszufiltern, während die NumLock-Taste unten ist oder während die NumLock-Taste (durch eine Sequenz aus NumLock-Taste auf und nieder) umgedreht wurde.

Intern setzt die Klasse CNumLock die folgenden Methoden ein, um den Status der NumLock-Taste zu ermitteln bzw. zu setzen.

Datenmember m_bIgnoreKeyInput

int GetCurrNumLockState()const;

Die Methode GetCurrNumLockState() liefert den aktuellen Status der NumLock-Taste als „0“ für den Zustand „aus“ oder „2“ für den Zustand „an“, was sich leicht implementieren läßt.

int  GetCurrNumLockState()const
{ 
  return GetKeyState( VK_NUMLOCK ) & 0x0001;
};

Das Resultat kann direkt gegen den geforderten Zustand in m_iReqdNumLockState verglichen werden.

Methode GetCurr-NumLockState

void ToggleNumLock(); 

Mittels ToggleNumLock() kann der Status der NumLock-Taste umgedreht werden. Die dazu notwendige Vorgehensweise ist systemabhängig. Unter Windows 9x wird der Status gesetzt, indem ein Tastatur-Status-Array via SetKeyboardState() geschrieben wird.

void CNumLock::ToggleNumLock()
{
  _ASSERT( m_bIgnoreKeyInput == false );
  m_bIgnoreKeyInput = true;
 
  if( m_OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
  {
    BYTE KeyboardState[ 256 ];
    VERIFY( GetKeyboardState( KeyboardState ));
    KeyboardState[ VK_NUMLOCK ] = KeyboardState[ VK_NUMLOCK ] ^ 1;
    VERIFY( SetKeyboardState( KeyboardState ));
  }

Unter Windows NT ist hingegen eine NumLock-Tasten-Sequenz notwendig, die das Drücken der Taste simuliert.

  else if( m_OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT )
    { 
    keybd_event( VK_NUMLOCK, 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0 );
    keybd_event( VK_NUMLOCK, 0x45, KEYEVENTF_EXTENDEDKEY | 
                                   KEYEVENTF_KEYUP, 0 );
    }
  else
    ; // OS nicht unterstützt
 
  m_bIgnoreKeyInput = false;
}

Für den Zugriff von außen exportiert die Klasse nur wenige Methoden.

Methode ToggleNumLock

CNumLock( bool bAlwaysOn = true ); 

Der Konstruktor spezifiziert den gewünschten Status für die NumLock-Taste, so wie die Anwendung, die diese Klasse verwendet, ihn benötigt. Per Default ist die NumLock-Taste immer an.

Konstruktor CNumLock

void CheckNumLockState()

Die Methode CheckNumLockState () vergleicht den aktuellen Status der Taste NumLock mit den geforderten und kehrt den Zustand erforderlichenfalls über einen Aufruf von ToggleNumLock() um.

Zeigt m_bIgnoreKeyInput an, daß ein Eingriff nicht erfolgen soll, weil der Tastenzustand gerade geändert wird, erfolgt sofort ein Rücksprung.

void CNumLock::CheckNumLockState()
{
  if(m_bIgnoreKeyInput)
    return;

Anderenfalls erfolgt ein Vergleich zwischen vorhandenem und gefordertem Zustand.

  if( GetCurrNumLockState() == m_iReqdNumLockState )
    ; // nichts zu tun
  else ToggleNumLock();
}

Der Aufruf von CheckNumLockState() erfolgt im Rahmen des OnIdle-Ereignisses.

BOOL CNumLockApp::OnIdle(LONG lCount) 
{
  if( lCount <= 0 )
    m_NumLock.CheckNumLockState();
  return CWinApp::OnIdle(lCount);
}

Damit ist sichergestellt, daß zu jedem Zeitpunkt, zu dem der Zustandswechsel der NumLock-Taste feststellbar ist, diese Untersuchung auch vorgenommen wird.

Methode CheckNum-LockState

bool FilterMessage( const MSG* pMsg ) 

Die Methode FilterMessage() wird verwendet, um eintreffende WM_KEYDOWN-und WM_KEYUP-Botschaften daraufhin zu untersuchen, ob die NumLock-Taste betroffen ist.

Ist die NumLock-Taste gedrückt, werden alle weiteren Tastatureingaben ignoriert, bis die korrespondierende WM_KEYUP-Botschaft entdeckt wird. Dann wird der aktuelle Zustand der NumLock-Taste mit dem geforderten Zustand verglichen und notfalls korrigiert.

bool CNumLock::FilterMessage(const MSG* pMsg)
{
  if( pMsg->message == WM_KEYDOWN )
  {
    if( m_bIgnoreKeyInput )
      return true;
    else if( pMsg->wParam == VK_NUMLOCK )
      return m_bIgnoreKeyInput = true;
    else return false;
  }
  else if( pMsg->message == WM_KEYUP )
    {
      if( pMsg->wParam == VK_NUMLOCK )
      {
        m_bIgnoreKeyInput = false;
        CheckNumLockState();
        return true;
      }
    else return m_bIgnoreKeyInput;
    }
  else return false;
}

Als Ergebnis returniert die FilterMessage() TRUE, wenn die Nachricht bearbeitet wurde, ansonsten FALSE. Diese Methode ist für eine Verwendung in PreTranslateMessage() vorgesehen, wie das nachfolgende Beispiel zeigt.

BOOL CNumLockApp::PreTranslateMessage(MSG* pMsg) 
{
  if( m_NumLock.FilterMessage( pMsg ))
    return TRUE;
  elsereturn CWinApp::PreTranslateMessage(pMsg);
}

Bei Dialog-Anwendungen ist statt des Ereignisses OnIdle() der Event OnKickIdle() zu behandeln.

LRESULT CNumLockDlgDlg::OnKickIdle(WPARAM, LPARAM lCount)
{
  m_NumLock.CheckNumLockState();
  return 0;
}

Methode FilterMessage





Sachgebiet


© 2009-2012 by Alojado Publishing. Alle Rechte vorbehalten. Ausgewiesene Marken gehören ihren jeweiligen Eigentümern.
Mit der Benutzung dieser Seite erkennen Sie die Nutzungsbedingungen und die Datenschutzerklärung an. Der Betreiber übernimmt keine Haftung für den Inhalt verlinkter externer Internetseiten.
Seite erzeugt 2012-05-20 02:48:06 von textarchiv.alojado.de