4

I would like to programmatically check the value of, and be able to toggle num-lock. What's the simplest way to do that in C#?

The reason is that I want to verify num-lock is "ON" at program start.

Thanks

Roee Adler
  • 33,434
  • 32
  • 105
  • 133

3 Answers3

8

Check How to programmatically turn on the Numlock Key

using System;
using System.Runtime.InteropServices;

class SetNumlockKeyOn
{
    [StructLayout(LayoutKind.Sequential)]
    public struct INPUT
    {
        internal int type;
        internal short wVk;
        internal short wScan;
        internal int dwFlags;
        internal int time;
        internal IntPtr dwExtraInfo;
        int dummy1;
        int dummy2;
        internal int type1;
        internal short wVk1;
        internal short wScan1;
        internal int dwFlags1;
        internal int time1;
        internal IntPtr dwExtraInfo1;
        int dummy3;
        int dummy4;
}
[DllImport(“user32.dll”)]
static extern int SendInput(uint nInputs, IntPtr pInputs, int cbSize);

public static void SetNumlockOn()
{
    const int mouseInpSize = 28;//Hardcoded size of the MOUSEINPUT tag !!!
    INPUT input = new INPUT();
    input.type = 0x01; //INPUT_KEYBOARD
    input.wVk = 0x90; //VK_NUMLOCK
    input.wScan = 0;
    input.dwFlags = 0; //key-down
    input.time = 0;
    input.dwExtraInfo = IntPtr.Zero;

    input.type1 = 0x01;
    input.wVk1 = 0x90;
    input.wScan1 = 0;
    input.dwFlags1 = 2; //key-up
    input.time1 = 0;
    input.dwExtraInfo1 = IntPtr.Zero;

    IntPtr pI = Marshal.AllocHGlobal(mouseInpSize * 2);
    Marshal.StructureToPtr(input, pI, false);
    int result = SendInput(2, pI, mouseInpSize); //Hardcoded size of the MOUSEINPUT tag !!!

    //if (result == 0 || Marshal.GetLastWin32Error() != 0)
    // Console.WriteLine(Marshal.GetLastWin32Error());
    Marshal.FreeHGlobal(pI);
}
Arsen Mkrtchyan
  • 49,896
  • 32
  • 148
  • 184
  • your link is dead. – AndersK Feb 27 '18 at 04:28
  • Found content, copied... hope it helps ;) – Arsen Mkrtchyan Feb 27 '18 at 12:09
  • I have an Acer Aspire 15 that insists on turning off num-lock every time it goes to sleep (or wakes up - not sure which). I tried the InitialKeyboardIndicators registry trick but that doesn't fix it - the num pad acts like arrow keys every time I wake the computer and log in. So I built the code above into a tiny exe, and used Windows' Task Scheduler to run it whenever I unlock my laptop. Problem solved. Thanks so much! – NSFW Aug 23 '18 at 04:00
  • I believe you need to check BIOS for that settings, but you are welcome ) – Arsen Mkrtchyan Aug 23 '18 at 09:44
  • this answer works great. One question though: how do you know what the input size is? Does it differ on different computers? Documentation for `SendInput` just says it will fail if the size is wrong. – Matthew Dec 14 '18 at 16:35
  • @Matthew, try to calculate size with Marshal.SizeOf(typeof(INPUT)), I think it should calculate the size always correctly – Arsen Mkrtchyan Dec 18 '18 at 10:22
3

You can do this via P/Invoke with GetKeyboardState and keybd_event.

The MSDN page for keybd_event shows exactly how to toggle num-lock, as well as get it's state (in C++).

There are P/Invoke signitures available on pinvoke.net for keybd_event and GetKeyboardState.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
0

In addition to the answer given by Arsen:

There are problems with heap corruption in 64-bit builds. Programs using this code may crash at any point. To see this, enable the debug option "Enable Windows debug heap allocator". The debugger stops on calling FreeHGlobal.

It helps to calculate the size of the INPUT structure as follows.

int mouseInpSize = Marshal.SizeOf(input);
IntPtr pI = Marshal.AllocHGlobal(mouseInpSize);
Marshal.StructureToPtr(input, pI, false);
int result = SendInput(2, pI, mouseInpSize);
Marshal.FreeHGlobal(pI);