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.