Faking num lock, caps lock and scroll lock leds

Introduction

Some time ago I came across a post where the author wanted to turn on and off num lock, cap lock and scroll lock led light without actually toggling their state. This sounded challenging and impossible but after some googling I found a piece of code in C for achieving exactly what I was looking for. In this post I show how it works and how to port it to C#

Toggling Keyboard Indicators from C#

The C version is available at rohitab.com As you can see from the code toggling the keyboard indicators consists of three steps:

  • Define MS-DOS device name for the target keyboard.
  • Get handle of the target keyboard.
  • Send command to the handle for changing keyboard indicators.

The first two steps are the pretty straightforward and easy to interop from C#. In order to define an MS-DOS name we need to call DefineDosDevice method pass the target keyboard path, desired MS-DOS device name and DDD_RAW_TARGET_PATH flag. The C# code looks like this:

[DllImport("kernel32.dll")]
static extern bool DefineDosDevice(uint dwFlags, string lpDeviceName, string lpTargetPath);

private const int DddRawTargetPath = 0x00000001;

We can call it like this:

DefineDosDevice(DddRawTargetPath, "keyboard", "\\Device\\KeyboardClass0");

Once we have defined Dos device name we can now use CreateFile to get handle to it. After that we use DeviceIoControl method to send IOCTL_KEYBOARD_SET_INDICATORS control code which indicates that we want to set keyboard indicator status. IOCTL_KEYBOARD_SET_INDICATORS control code is defined in Ntddkbd.h using CTL_CODE macro. In C# we can define it like this:

private const uint FileAnyAccess = 0;
private const uint MethodBuffered = 0;
private const uint FileDeviceKeyboard = 0x0000000b;

private static uint IOCTL_KEYBOARD_SET_INDICATORS = 
               ControlCode(FileDeviceKeyboard, 0x0002, MethodBuffered, FileAnyAccess);

static uint ControlCode(uint deviceType, uint function, uint method, uint access)
{
    return ((deviceType) << 16) | ((access) << 14) | ((function) << 2) | (method);
}

The information about which indicators should be on and which should be off is specified by a KEYBOARD_INDICATOR_PARAMETERS structure.

The final code looks like this:

private static uint IOCTL_KEYBOARD_SET_INDICATORS = ControlCode(FileDeviceKeyboard, 0x0002, MethodBuffered, FileAnyAccess);

private const uint FileAnyAccess = 0;
private const uint MethodBuffered = 0;
private const uint FileDeviceKeyboard = 0x0000000b;
private const int DddRawTargetPath = 0x00000001;

public static void ToggleLights(Locks locks)
{
    DefineDosDevice(DddRawTargetPath, "keyboard", "\\Device\\KeyboardClass0");

    var indicators = new KeyboardIndicatorParameters();

    using (var hKeybd = CreateFile("\\\\.\\keyboard", FileAccess.Write, 0, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero))
    {
        indicators.UnitId = 0;
        indicators.LedFlags = locks;

        var size = Marshal.SizeOf(typeof(KeyboardIndicatorParameters));

        uint bytesReturned;
        DeviceIoControl(hKeybd, IOCTL_KEYBOARD_SET_INDICATORS, ref indicators, (uint)size,
            IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);
    }
}

static uint ControlCode(uint deviceType, uint function, uint method, uint access)
{
    return ((deviceType) << 16) | ((access) << 14) | ((function) << 2) | (method);
}

struct KeyboardIndicatorParameters
{
    public ushort UnitId;
    public Locks LedFlags;
}

[Flags]
public enum Locks : ushort
{
    None = 0,
    KeyboardScrollLockOn = 1,
    KeyboardNumLockOn = 2,
    KeyboardCapsLockOn = 4
}

Conclusion

Even though toggling keyboard indicators may not be very useful porting it to C# was interesting and fun. The only real application that I was able to find is an addon for Miranda IM which uses it as a notification mechanism.

Avatar
Giorgi Dalakishvili
World-Class Software Engineer

Related