
Introduction
I have a status bar that toggles the Num Lock, Caps Lock and
Scroll Lock keys in one application but some time ago I discovered that
the API function SendInput I used for it requires Windows 98 or
later and Windows NT 4.0 with Service Pack 3.0 or later. I have browsed MSDN
and found that there are 2 more functions that can toggle keyboard state but
each of them have some specific limitations like SendInput does.
To make it easier for others I have posted all possible variants with their
descriptions and limitations and a simple solution for toggling these keys avoiding
compatibility problems.
Variant A - using keybd_event
Description:
The keybd_event function synthesizes a keystroke. The system can
use such a synthesized keystroke to generate a WM_KEYUP or WM_KEYDOWN
message. The keyboard driver's interrupt handler calls the keybd_event
function.
Version Computability:
Windows NT/2000/XP: Requires Windows NT 3.1 or later.
Windows 95/98/ME: Requires Windows 95 or later.
Windows CE: Requires version 1.0 or later.
Important Notes:
- An application can simulate a press of the PRINTSCRN key in order to obtain
a screen snapshot and save it to the clipboard. To do this, call
keybd_event
with the first parameter set to VK_SNAPSHOT.
- Windows 95/98/Me: The
keybd_event function can toggle only the CAPS LOCK
and SCROLL LOCK keys. It cannot toggle the NUM LOCK key. We can do this with
SendInput but it requires Windows NT 4.0 SP3 and later or Windows 98 and later.
- Windows NT/2000/XP: The
keybd_event function can toggle the
NUM LOCK, CAPS LOCK, and SCROLL LOCK keys.
Sample Usage
When setting key state be sure to generate both key press and key release (see
below). Notes: Windows NT/2000: This function has been superseded. Use SendInput
instead.
::keybd_event( VK_CAPITAL, 0x45, KEYEVENTF_EXTENDEDKEY, 0 );
::keybd_event( VK_CAPITAL, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0 );
Variant B - using SendInput
Description:
The SendInput function synthesizes keystrokes, mouse motions, and button clicks.
Version Compatibility:
Windows NT/2000/XP: Requires Windows NT 4.0 SP3 or later.
Windows 95/98/Me: Requires Windows 98.
Windows CE: Requires version 2.0 or later.
Sample Usage:
When setting key state be sure to generate both key press and key release (see
below). And do not forget to include WinAble.h, not WinUser.h as MSDN says.
#include <WinAble.h> // Required for the ::SendInput function
INPUT input[2];
::ZeroMemory(input, sizeof(input));
input[0].type = input[1].type = INPUT_KEYBOARD;
input[0].ki.wVk = input[1].ki.wVk = VK_CAPITAL;
input[1].ki.dwFlags = KEYEVENTF_KEYUP; ::SendInput(2, input, sizeof(INPUT));
Variant C - using SetKeyboardState
Description:
The SetKeyboardState function copies a 256-byte array of keyboard
key states into the calling thread's keyboard-input state table.
Version Compatibility:
Windows NT/2000/XP: Requires Windows NT 3.1 or later.
Windows 95/98/Me: Requires Windows 95 or later.
Windows CE: Unsupported.
Important Notes:
- Because the
SetKeyboardState function alters the input state
of the calling thread and not the global input state of the system, an application
cannot use SetKeyboardState to set the NUM LOCK, CAPS LOCK, or
SCROLL LOCK indicator lights on the keyboard.
SetKeyboardState function sets keyboard state for calling thread
only, not the global input state of the system.
Sample Usage:
BYTE byKeybState[256];
::GetKeyboardState(byKeybState);
byKeybState[nVirtKey] = !(BOOL)::GetKeyState(VK_CAPITAL);
::SetKeyboardState(byKeybState);
Support for setting Num Lock, Caps Lock and Scroll Lock Keys State
|
Function\OS
|
Win CE 1.0
|
Win CE 2.0
|
Win 95
|
Win 98/Me
|
Win NT
|
Win 2000/XP
|
|
keybd_event
|
Yes
|
Yes
|
All but Num Lock
|
All but Num Lock
|
Yes
|
Yes
|
|
SendInput
|
|
Yes
|
|
Yes
|
SP3*
|
Yes
|
|
SetKeyboardState
|
|
|
Yes**
|
Yes**
|
Yes**
|
Yes**
|
* - Requires Service Pack 3 or later.
** - Does not set indicator lights on the keyboard and toggles input state of
the calling thread only.
Simple Solution

I have provided a simple StatusBar class that toggles the keys when the appropriate
pane is double-clicked. See below:
#include <WinAble.h>
void CStatusBarEx::OnNMDblclk(NMHDR* pNMHDR, LRESULT *pResult)
{
NMMOUSE* pNMMOUSE = (NMMOUSE*)pNMHDR;
INT nInd = GetItemID((int)pNMMOUSE->dwItemSpec);
if((nInd >= ID_INDICATOR_CAPS) &&
(nInd <= ID_INDICATOR_OVR))
{
UINT nVirtKey = 0;
switch(nInd)
{
case ID_INDICATOR_CAPS: nVirtKey = VK_CAPITAL; break;
case ID_INDICATOR_NUM: nVirtKey = VK_NUMLOCK; break;
case ID_INDICATOR_SCRL: nVirtKey = VK_SCROLL; break;
case ID_INDICATOR_OVR: nVirtKey = VK_INSERT; break;
}
ASSERT(nVirtKey >= 1 && nVirtKey<= 254);
DWORD dwVersion = ::GetVersion();
if( dwVersion < 0x80000000) {
::keybd_event( nVirtKey, 0x45, KEYEVENTF_EXTENDEDKEY, 0 );
::keybd_event( nVirtKey, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0 );
}
else
{
DWORD dwMin = (DWORD)(HIBYTE(LOWORD(dwVersion)));
if(dwMin >= 10) {
INPUT input[2];
::ZeroMemory(input, sizeof(input));
input[0].type = input[1].type = INPUT_KEYBOARD;
input[0].ki.wVk = input[1].ki.wVk = nVirtKey;
input[1].ki.dwFlags = KEYEVENTF_KEYUP; ::SendInput(2, input, sizeof(INPUT));
}
else {
BYTE byKeybState[256];
::GetKeyboardState(byKeybState);
byKeybState[nVirtKey] = !(BOOL)::GetKeyState(nVirtKey);
::SetKeyboardState(byKeybState);
}
}
::Beep(500, 100);
}
else
{
CWnd* pWnd = GetParent();
if(pWnd != NULL)
{
}
}
*pResult = 0;
}
Please send me all bugs, fixes or better solutions.