Manipulating the Keyboard Lights in Windows NT

Environment: VC5, NT4 SP3-SP5

This code demonstrates manipulation of the keyboard indicator lights in Windows NT 4.0. A few relevant values were borrowed from the DDK headers, so it is not necessary to have the DDK installed, to build these sources.

There's really nothing fancy about this code, it creates a DOS device for the keyboard device, opens a handle to that device, and calls DeviceIoControl to send IOCTL function codes to the device.

It did require a fair amount digging around in the DDK to create this effect, and it is an interesting twist on an alternate UI mechanism, and thus I felt it might be suitable for inclusion in this archive.

A high-level, multi-threaded function to flash a keyboard light continuously in its own thread, is included. This call is demonstrated in the sample project.

NTKbdLites can be easily built for inclusion in a static link library by defining STATIC_LIBRARY on the compiler command line or in settings.


/***********************************************************
** NTKbdLites.c                                           **
**                                                        **
**  Copyright 1999 Mark J. McGinty, All Rights Reserved   **
**   Free Usage granted to the public domain.             **
**                                                        **
***********************************************************/

#include <windows.h>
#include <winioctl.h>
#include "NTKbdLites.h"

// Code can be built as either a DLL or a static link library
//
#ifdef STATIC_LIBRARY
#define DECLSPEC 
#else
#define DECLSPEC __declspec(dllexport)
#endif

// FlashKeyboardLight
//
// Flashes the keyboard indicator, specified by LightFlag, one time, 
// at the rate indicated by Duration. All lights are left in their
// previous states when this call returns.
//
// Possible LightFlags:
//      KEYBOARD_CAPS_LOCK_ON   
//      KEYBOARD_NUM_LOCK_ON    
//      KEYBOARD_SCROLL_LOCK_ON

int DECLSPEC FlashKeyboardLight(HANDLE hKbdDev, UINT LedFlag, int Duration)
{
    KEYBOARD_INDICATOR_PARAMETERS InputBuffer;    // Input buffer for DeviceIoControl
    KEYBOARD_INDICATOR_PARAMETERS OutputBuffer;   // Output buffer for DeviceIoControl
    UINT                LedFlagsMask;
    BOOL                Toggle;
    ULONG               DataLength = sizeof(KEYBOARD_INDICATOR_PARAMETERS);
    ULONG               ReturnedLength; // Number of bytes returned in output buffer
    int             i;

    InputBuffer.UnitId = 0;
    OutputBuffer.UnitId = 0;

    // Preserve current indicators' state
    //
    if (!DeviceIoControl(hKbdDev, IOCTL_KEYBOARD_QUERY_INDICATORS,
                &InputBuffer, DataLength,
                &OutputBuffer, DataLength,
                &ReturnedLength, NULL))
        return GetLastError();

    // Mask bit for light to be manipulated
    //
    LedFlagsMask = (OutputBuffer.LedFlags & (~LedFlag));

    // Set toggle variable to reflect current state.
    //
    Toggle = (OutputBuffer.LedFlags & LedFlag);

    for (i = 0; i < 2; i++)
    {
        Toggle ^= 1;
        InputBuffer.LedFlags = (LedFlagsMask | (LedFlag * Toggle));

        if (!DeviceIoControl(hKbdDev, IOCTL_KEYBOARD_SET_INDICATORS,
                    &InputBuffer, DataLength,
                    NULL,   0,  &ReturnedLength, NULL))
            return GetLastError();

        Sleep(Duration);
    }
    return 0;
}

HANDLE DECLSPEC OpenKeyboardDevice(int *ErrorNumber)
{
    HANDLE  hndKbdDev;
    int     *LocalErrorNumber;
    int     Dummy;

    if (ErrorNumber == NULL)
        LocalErrorNumber = &Dummy
    else
        LocalErrorNumber = ErrorNumber;

    *LocalErrorNumber = 0;
    
    if (!DefineDosDevice (DDD_RAW_TARGET_PATH, "Kbd",
                "\\Device\\KeyboardClass0"))
    {
        *LocalErrorNumber = GetLastError();
        return INVALID_HANDLE_VALUE;
    }

    hndKbdDev = CreateFile("\\\\.\\Kbd", GENERIC_WRITE, 0,
                NULL,   OPEN_EXISTING,  0,  NULL);
    
    if (hndKbdDev == INVALID_HANDLE_VALUE)
        *LocalErrorNumber = GetLastError();

    return hndKbdDev;
}

int DECLSPEC CloseKeyboardDevice(HANDLE hndKbdDev)
{
    int e = 0;

    if (!DefineDosDevice (DDD_REMOVE_DEFINITION, "Kbd", NULL))
        e = GetLastError();

    if (!CloseHandle(hndKbdDev))                    
        e = GetLastError();

    return e;
}

// Thread procedure to make a light flash continuously.
//
DWORD WINAPI FlashKeyboardLightThd(LPVOID lpv)
{
    LPFLASH_KBD_THD_INIT pInit = (LPFLASH_KBD_THD_INIT)lpv;
    FLASH_KBD_THD_INIT Init = *pInit;
    HANDLE  hndKbdDev;
    HANDLE  heventCancel = OpenEvent(EVENT_ALL_ACCESS, FALSE, Init.EventName);

    if (heventCancel == NULL)
        ExitThread(-1);

    hndKbdDev = OpenKeyboardDevice(NULL);
    if (hndKbdDev == INVALID_HANDLE_VALUE)
    {
        CloseHandle(heventCancel);
        ExitThread(-1);
    }

    for (;;)
    {
        FlashKeyboardLight(hndKbdDev, Init.LightFlag, Init.Duration);
        
        if (WaitForSingleObject(heventCancel, Init.Duration) != WAIT_TIMEOUT)
            break;
    }

    Sleep(Init.Duration);

    CloseHandle(heventCancel);
    CloseKeyboardDevice(hndKbdDev);
    
    ExitThread(0);
    return 0;
}

// Builds structure and creates thread, to flash light continuously
//
HANDLE DECLSPEC FlashKeyboardLightInThread(UINT LightFlag, int Duration, LPSTR EventName)
{
    DWORD ThreadId;
    static FLASH_KBD_THD_INIT FlashInit;

    FlashInit.LightFlag = LightFlag;
    FlashInit.Duration = Duration;
    lstrcpyn(FlashInit.EventName, EventName, 128);

    return CreateThread(NULL, 0, FlashKeyboardLightThd, (LPVOID)&FlashInit, 0, &ThreadId);
 
}

/***********************************************************
** NTKbdLites.h                                           **
**                                                        **
**  Copyright 1999 Mark J. McGinty, All Rights Reserved   **
**   Free Usage granted to the public domain.             **
**                                                        **
***********************************************************/

#include <windows.h>

//
// Define the keyboard indicators.
//

#define IOCTL_KEYBOARD_SET_INDICATORS        CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0002, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_KEYBOARD_QUERY_TYPEMATIC       CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0008, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_KEYBOARD_QUERY_INDICATORS      CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0010, METHOD_BUFFERED, FILE_ANY_ACCESS)


typedef struct _KEYBOARD_INDICATOR_PARAMETERS {
    USHORT UnitId;          // Unit identifier.
    USHORT    LedFlags;     // LED indicator state.

} KEYBOARD_INDICATOR_PARAMETERS, *PKEYBOARD_INDICATOR_PARAMETERS;

#define KEYBOARD_CAPS_LOCK_ON     4
#define KEYBOARD_NUM_LOCK_ON      2
#define KEYBOARD_SCROLL_LOCK_ON   1

#ifdef STATIC_LIBRARY
#define DECLSPEC 
#else
#define DECLSPEC __declspec(dllexport)
#endif

int DECLSPEC FlashKeyboardLight(HANDLE hKbdDev, UINT LightFlag, int Duration);
HANDLE DECLSPEC OpenKeyboardDevice(int *ErrorNumber);
int DECLSPEC CloseKeyboardDevice(HANDLE hndKbdDev);
HANDLE DECLSPEC FlashKeyboardLightInThread(UINT, int, LPSTR);

typedef struct {
    UINT        LightFlag;
    int     Duration;
    char        EventName[128];
} FLASH_KBD_THD_INIT, *LPFLASH_KBD_THD_INIT;

/*********************************************************** ** NTFlashScrollLight.c ** ** ** ** Copyright 1999 Mark J. McGinty, All Rights Reserved ** ** Free Usage granted to the public domain. ** ** ** ** ** ** A short test program, to excersize NTKbdLites.c ** ** ** ***********************************************************/ #include <windows.h> #include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <conio.h> #include "NTKbdLites.h" #define CANCEL_EVENT_NAME "TestThreadedFlasherEvent" void __cdecl main(int argc, char **argv) { HANDLE heventCancel = CreateEvent(NULL, FALSE, FALSE, CANCEL_EVENT_NAME); HANDLE hThread = FlashKeyboardLightInThread(KEYBOARD_SCROLL_LOCK_ON, 250, CANCEL_EVENT_NAME); printf("\r\n\r\nSample usage of IOCTL_KEYBOARD_SET_INDICATORS"); printf("\r\n\r\n (the Scroll Lock light should be flashing)"); printf("\r\n\r\n\r\npress any key to exit..."); getch(); printf("\r\n"); SetEvent(heventCancel); WaitForSingleObject(hThread, 30000); CloseHandle(heventCancel); exit(0); }

Downloads

Download demo project - 24 Kb
Download source - 6 Kb


About the Author

Mark McGinty

Programming professionally for 15 years I've used a wide range of languages, including C/C++, x86 assembler, JScript, numerous forms of VB, SmallTalk and T-SQL, to name a few. Much of what I do these days involves databases to some degree, mostly back-end database/application architecture for data-driven websites.

Comments

  • alternative to Linux's "Tleds"

    Posted by Legacy on 12/20/2001 12:00am

    Originally posted by: hurde

    Does anyone know if there's a windows app that uses the keyboard leds to display Rx and Tx activity ?
    (like Tleds for Linux)

    thx

    Reply
  • or just do this:

    Posted by Legacy on 10/29/2001 12:00am

    Originally posted by: anon


    (this example is for the numlock but could be changed to any of the other lights. Could be called on an OnTimer event)

    void SetNumLock( BOOL bState )
    {
    BYTE keyState[256];

    GetKeyboardState((LPBYTE)&keyState);

    if ( (bState && !(keyState[VK_NUMLOCK] & 1)) || (!bState && (keyState[VK_NUMLOCK] & 1)) )
    {
    // Simulate a key press
    keybd_event( VK_NUMLOCK, 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0 );

    // Simulate a key release
    keybd_event( VK_NUMLOCK, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
    }
    }

    • Not the same

      Posted by mmcginty on 07/29/2006 03:10am

      1. It makes no attempt to preserve/restore the original state;
      2. It alters the behavioral state of the keyboard (my code only alters the LED indicators);
      3. Check me if I'm wrong, but the conditional expression appears to be inconsequential;

      -MM

      Reply
    Reply
  • Any ideas for Win9x

    Posted by Legacy on 10/01/2001 12:00am

    Originally posted by: Ryan Binns

    Win9x does not support the DefineDosDevice() call...

    Any ideas on how to do the same thing in Win9x?

    Thanks,
    Ryan

    Reply
  • C++

    Posted by Legacy on 05/22/2001 12:00am

    Originally posted by: Thanos P

    Could someone please email me a simple c++ version of this demo? Ive been working on making it work for a week straight now, and i must be missing something really obvious or something! Thanks!

    foffe1@hushmail.com

    Reply
  • Manipulating the Keyboard Lights in Windows NT

    Posted by Legacy on 12/23/1999 12:00am

    Originally posted by: Petr havlicek

    That's interesting. When I seen that I remember the "Golden Assembler time" :-) Thanx for suggestion how to use calling the dos devices under the NT.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • As all sorts of data becomes available for storage, analysis and retrieval - so called 'Big Data' - there are potentially huge benefits, but equally huge challenges...
  • The agile organization needs knowledge to act on, quickly and effectively. Though many organizations are clamouring for "Big Data", not nearly as many know what to do with it...
  • Cloud-based integration solutions can be confusing. Adding to the confusion are the multiple ways IT departments can deliver such integration...

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date