在不实际打开大写锁定的情况下打开键盘大写锁定指示灯的方法

发布于 2024-08-21 09:42:06 字数 357 浏览 11 评论 0原文

我正在编写一个使用 Caps Lock 作为切换开关的程序。如果能设置按键的 LED 来显示我的程序打开或关闭,就像 Caps Lock 键自然所做的那样,那就太好了。

我知道我可以通过 SendInput('Capslock'); 或其他任何方式来实际打开和关闭大写锁定。但我的应用程序是一个打字程序,我不想处理翻译全大写键,打开它会让我转换成小写/大写。我最终可能会走这条路,但不是这个版本。

不过,我有兴趣只打开 LED 灯,而不实际打开 Caps Lock。有办法做到这一点吗?

I'm writing a program that uses Caps Lock as a toggle switch. It would be nice to set the LED of the key to show that my program is on or off, like the Caps Lock key does naturally.

I know that I could just SendInput('Capslock'); or whatever to actually turn caps-lock on and off. But my application is a typing program, and I don't want to have to deal with translating the all-caps keys that turning it on would give me into their lower/upper cases. I might go that route eventually, but not for this version.

I would however be interested in just turning on the LED light without actually turning on Caps Lock. Is there a way to do that?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

烟花肆意 2024-08-28 09:42:06

Miranda IM 有一个名为“Keyboard Notify Ext." 其源代码中包含控制 LED 的 C 实现。请参阅源代码中的文件 keyboard.c。也许您可以将其移植到 C#。

以下是源代码中最有趣的亮点:

mir_snprintf(aux1, sizeof(aux1), "Kbd%d", i);
mir_snprintf(aux2, sizeof(aux2), "\\Device\\KeyboardClass%d", i);
DefineDosDevice(DDD_RAW_TARGET_PATH, aux1, aux2);

mir_snprintf(aux1, sizeof(aux1), "\\\\.\\Kbd%d", i);
hKbdDev[i] = CreateFile(aux1, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

...

result |= DeviceIoControl(hKbdDev[i], IOCTL_KEYBOARD_SET_INDICATORS, &InputBuffer, DataLength, NULL, 0, &ReturnedLength, NULL);

There is a plugin for Miranda IM named "Keyboard Notify Ext." which contains in its source code a C implementation of controlling LEDs. See file keyboard.c in the source code. Probably you can port it to C#.

Here are most interesting highlights from source code:

mir_snprintf(aux1, sizeof(aux1), "Kbd%d", i);
mir_snprintf(aux2, sizeof(aux2), "\\Device\\KeyboardClass%d", i);
DefineDosDevice(DDD_RAW_TARGET_PATH, aux1, aux2);

mir_snprintf(aux1, sizeof(aux1), "\\\\.\\Kbd%d", i);
hKbdDev[i] = CreateFile(aux1, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

...

result |= DeviceIoControl(hKbdDev[i], IOCTL_KEYBOARD_SET_INDICATORS, &InputBuffer, DataLength, NULL, 0, &ReturnedLength, NULL);
讽刺将军 2024-08-28 09:42:06

我很确定你不能在不切换实际大写锁定的情况下切换 LED,除非你正在编写键盘驱动程序。 (我不建议这样做!)

I'm pretty sure you can't toggle the LED without toggling the actual caps lock, unless you were writing a keyboard driver. (I'm not recommending that!)

此刻的回忆 2024-08-28 09:42:06

有点晚了,但以防万一其他人偶然发现这一点,这里有一个翻转 Caps Lock LED 的 C# 程序。它基于 @ivanzoid 的答案(以及更多随机的谷歌搜索)。

基本过程是

  • 获取键盘设备的句柄
  • 读取当前 LED 状态
  • 翻转与 Caps Lock LED 对应的位<--这是您可以执行其他随机操作的地方
  • 将状态写回键盘
  • 清理
// to compile: powershell> C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc /out:capsblink.exe /target:exe capsblink.cs
//
// Following:
//  - original motivation: https://stackoverflow.com/questions/72679665/is-it-possible-to-control-capslock-light-without-actual-capslocking/72679984#72679984
//  - partial answer using C-API: https://stackoverflow.com/questions/2248358/way-to-turn-on-keyboards-caps-lock-light-without-actually-turning-on-caps-lock
//  - full version of the partial answer from above (in C, 1999): https://www.codeguru.com/windows/manipulating-the-keyboard-lights-in-windows-nt/
//  - calling C-API from C# via p/invoke: https://learn.microsoft.com/en-us/archive/msdn-magazine/2003/july/net-column-calling-win32-dlls-in-csharp-with-p-invoke
//  - Win32 API docs: https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol (and related)

using System.Runtime.InteropServices;
using System.ComponentModel;
using System;

class CapsLockLight
{
    [DllImport("kernel32.dll", SetLastError=true)]
    public static extern Boolean DefineDosDevice(UInt32 flags, String deviceName, String targetPath);

    [DllImport("kernel32.dll", SetLastError=true)]
    public static extern IntPtr CreateFile(String fileName,
                       UInt32 desiredAccess, UInt32 shareMode, IntPtr securityAttributes,
                       UInt32 creationDisposition, UInt32 flagsAndAttributes, IntPtr templateFile
                      );

    [StructLayout(LayoutKind.Sequential)]
    public struct KEYBOARD_INDICATOR_PARAMETERS
    {
        public UInt16 unitID;
        public UInt16 LEDflags;
    }

    [DllImport("kernel32.dll", SetLastError=true)]
    public static extern Boolean DeviceIoControl(IntPtr device, UInt32 ioControlCode,
                          ref KEYBOARD_INDICATOR_PARAMETERS KIPin,  UInt32  inBufferSize,
                          ref KEYBOARD_INDICATOR_PARAMETERS KIPout, UInt32 outBufferSize,
                          ref UInt32 bytesReturned, IntPtr overlapped
                         );
    [DllImport("kernel32.dll", SetLastError=true)]
    public static extern Boolean DeviceIoControl(IntPtr device, UInt32 ioControlCode,
                          IntPtr KIPin,  UInt32  inBufferSize,
                          ref KEYBOARD_INDICATOR_PARAMETERS KIPout, UInt32 outBufferSize,
                          ref UInt32 bytesReturned, IntPtr overlapped
                         );
    [DllImport("kernel32.dll", SetLastError=true)]
    public static extern Boolean DeviceIoControl(IntPtr device, UInt32 ioControlCode,
                          ref KEYBOARD_INDICATOR_PARAMETERS KIPin,  UInt32  inBufferSize,
                          IntPtr KIPout, UInt32 outBufferSize,
                          ref UInt32 bytesReturned, IntPtr overlapped
                         );

    [DllImport("kernel32.dll", SetLastError=true)]
    public static extern Boolean CloseHandle(IntPtr handle);

    static void Main(string[] args)
    {
        UInt32 bytesReturned = 0;
        IntPtr device;
        KEYBOARD_INDICATOR_PARAMETERS KIPbuf = new KEYBOARD_INDICATOR_PARAMETERS { unitID = 0, LEDflags = 0 };

        if(!DefineDosDevice(Flags.DDD_RAW_TARGET_PATH, "myKBD", "\\Device\\KeyboardClass0"))
        {
            Int32 err = Marshal.GetLastWin32Error();
            throw new Win32Exception(err);
        }

        // Console.WriteLine("Created device");

        device = CreateFile("\\\\.\\myKBD", Flags.GENERIC_WRITE, 0, IntPtr.Zero, Flags.OPEN_EXISTING, 0, IntPtr.Zero);
        if(device == Flags.INVALID_HANDLE_VALUE)
        {
            Int32 err = Marshal.GetLastWin32Error();
            throw new Win32Exception(err);
        }

        // Console.WriteLine("Opened device");

        if(!DeviceIoControl(device, Flags.IOCTL_KEYBOARD_QUERY_INDICATORS, IntPtr.Zero, 0, ref KIPbuf, (UInt32)Marshal.SizeOf(KIPbuf), ref bytesReturned, IntPtr.Zero))
        {
            Int32 err = Marshal.GetLastWin32Error();
            throw new Win32Exception(err);
        }

        // Console.WriteLine(String.Format("Read LED status: {0:x}", KIPbuf.LEDflags));

        KIPbuf.LEDflags = (UInt16)(KIPbuf.LEDflags ^ Flags.KEYBOARD_CAPS_LOCK_ON);

        // Console.WriteLine(String.Format("Changed LED status to: {0:x}", KIPbuf.LEDflags));

        if(!DeviceIoControl(device, Flags.IOCTL_KEYBOARD_SET_INDICATORS, ref KIPbuf, (UInt32)Marshal.SizeOf(KIPbuf), IntPtr.Zero, 0, ref bytesReturned, IntPtr.Zero))
        {
            Int32 err = Marshal.GetLastWin32Error();
            throw new Win32Exception(err);
        }

        // Console.WriteLine("Set new LED status");

        if(!CloseHandle(device))
        {
            Int32 err = Marshal.GetLastWin32Error();
            throw new Win32Exception(err);
        }

        // Console.WriteLine("Closed device handle");

        if(!DefineDosDevice(Flags.DDD_REMOVE_DEFINITION, "myKBD", null))
        {
            Int32 err = Marshal.GetLastWin32Error();
            throw new Win32Exception(err);
        }

        // Console.WriteLine("Removed device definition");
    }
};

class Flags
{
    public static IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);
    public const UInt32 IOCTL_KEYBOARD_SET_INDICATORS   = (0x0000000b << 16) | (0 << 14) | (0x0002 << 2) | 0; // from ntddkbd.h, ntddk.h
    public const UInt32 IOCTL_KEYBOARD_QUERY_INDICATORS = (0x0000000b << 16) | (0 << 14) | (0x0010 << 2) | 0; // from ntddkbd.h, ntddk.h

    public const UInt32 DDD_RAW_TARGET_PATH       = 0x00000001;
    public const UInt32 DDD_REMOVE_DEFINITION     = 0x00000002;
    public const UInt32 DDD_EXACT_MATCH_ON_REMOVE = 0x00000004;
    public const UInt32 DDD_NO_BROADCAST_SYSTEM   = 0x00000008;

    public const UInt32 GENERIC_ALL     = 0x10000000;
    public const UInt32 GENERIC_EXECUTE = 0x20000000;
    public const UInt32 GENERIC_WRITE   = 0x40000000;
    public const UInt32 GENERIC_READ    = 0x80000000;

    public const UInt32 CREATE_NEW        = 1;
    public const UInt32 CREATE_ALWAYS     = 2;
    public const UInt32 OPEN_EXISTING     = 3;
    public const UInt32 OPEN_ALWAYS       = 4;
    public const UInt32 TRUNCATE_EXISTING = 5;

    public const UInt16 KEYBOARD_SCROLL_LOCK_ON = 1;
    public const UInt16 KEYBOARD_NUM_LOCK_ON    = 2;
    public const UInt16 KEYBOARD_CAPS_LOCK_ON   = 4;
    public const UInt16 KEYBOARD_SHADOW         = 0x4000;
    public const UInt16 KEYBOARD_LED_INJECTED   = 0x8000;
};

A bit late to the party, but in case anyone else stumbles across this, here is a C# program flipping the Caps Lock LED. It is based on @ivanzoid's answer (and a lot more random Googling).

The basic procedure is

  • obtain a handle for the keyboard device
  • read the current LED status
  • flip the bit corresponding to the Caps Lock LED <-- this is the place where you could do other random things
  • write the status back to the keyboard
  • clean up
// to compile: powershell> C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc /out:capsblink.exe /target:exe capsblink.cs
//
// Following:
//  - original motivation: https://stackoverflow.com/questions/72679665/is-it-possible-to-control-capslock-light-without-actual-capslocking/72679984#72679984
//  - partial answer using C-API: https://stackoverflow.com/questions/2248358/way-to-turn-on-keyboards-caps-lock-light-without-actually-turning-on-caps-lock
//  - full version of the partial answer from above (in C, 1999): https://www.codeguru.com/windows/manipulating-the-keyboard-lights-in-windows-nt/
//  - calling C-API from C# via p/invoke: https://learn.microsoft.com/en-us/archive/msdn-magazine/2003/july/net-column-calling-win32-dlls-in-csharp-with-p-invoke
//  - Win32 API docs: https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol (and related)

using System.Runtime.InteropServices;
using System.ComponentModel;
using System;

class CapsLockLight
{
    [DllImport("kernel32.dll", SetLastError=true)]
    public static extern Boolean DefineDosDevice(UInt32 flags, String deviceName, String targetPath);

    [DllImport("kernel32.dll", SetLastError=true)]
    public static extern IntPtr CreateFile(String fileName,
                       UInt32 desiredAccess, UInt32 shareMode, IntPtr securityAttributes,
                       UInt32 creationDisposition, UInt32 flagsAndAttributes, IntPtr templateFile
                      );

    [StructLayout(LayoutKind.Sequential)]
    public struct KEYBOARD_INDICATOR_PARAMETERS
    {
        public UInt16 unitID;
        public UInt16 LEDflags;
    }

    [DllImport("kernel32.dll", SetLastError=true)]
    public static extern Boolean DeviceIoControl(IntPtr device, UInt32 ioControlCode,
                          ref KEYBOARD_INDICATOR_PARAMETERS KIPin,  UInt32  inBufferSize,
                          ref KEYBOARD_INDICATOR_PARAMETERS KIPout, UInt32 outBufferSize,
                          ref UInt32 bytesReturned, IntPtr overlapped
                         );
    [DllImport("kernel32.dll", SetLastError=true)]
    public static extern Boolean DeviceIoControl(IntPtr device, UInt32 ioControlCode,
                          IntPtr KIPin,  UInt32  inBufferSize,
                          ref KEYBOARD_INDICATOR_PARAMETERS KIPout, UInt32 outBufferSize,
                          ref UInt32 bytesReturned, IntPtr overlapped
                         );
    [DllImport("kernel32.dll", SetLastError=true)]
    public static extern Boolean DeviceIoControl(IntPtr device, UInt32 ioControlCode,
                          ref KEYBOARD_INDICATOR_PARAMETERS KIPin,  UInt32  inBufferSize,
                          IntPtr KIPout, UInt32 outBufferSize,
                          ref UInt32 bytesReturned, IntPtr overlapped
                         );

    [DllImport("kernel32.dll", SetLastError=true)]
    public static extern Boolean CloseHandle(IntPtr handle);

    static void Main(string[] args)
    {
        UInt32 bytesReturned = 0;
        IntPtr device;
        KEYBOARD_INDICATOR_PARAMETERS KIPbuf = new KEYBOARD_INDICATOR_PARAMETERS { unitID = 0, LEDflags = 0 };

        if(!DefineDosDevice(Flags.DDD_RAW_TARGET_PATH, "myKBD", "\\Device\\KeyboardClass0"))
        {
            Int32 err = Marshal.GetLastWin32Error();
            throw new Win32Exception(err);
        }

        // Console.WriteLine("Created device");

        device = CreateFile("\\\\.\\myKBD", Flags.GENERIC_WRITE, 0, IntPtr.Zero, Flags.OPEN_EXISTING, 0, IntPtr.Zero);
        if(device == Flags.INVALID_HANDLE_VALUE)
        {
            Int32 err = Marshal.GetLastWin32Error();
            throw new Win32Exception(err);
        }

        // Console.WriteLine("Opened device");

        if(!DeviceIoControl(device, Flags.IOCTL_KEYBOARD_QUERY_INDICATORS, IntPtr.Zero, 0, ref KIPbuf, (UInt32)Marshal.SizeOf(KIPbuf), ref bytesReturned, IntPtr.Zero))
        {
            Int32 err = Marshal.GetLastWin32Error();
            throw new Win32Exception(err);
        }

        // Console.WriteLine(String.Format("Read LED status: {0:x}", KIPbuf.LEDflags));

        KIPbuf.LEDflags = (UInt16)(KIPbuf.LEDflags ^ Flags.KEYBOARD_CAPS_LOCK_ON);

        // Console.WriteLine(String.Format("Changed LED status to: {0:x}", KIPbuf.LEDflags));

        if(!DeviceIoControl(device, Flags.IOCTL_KEYBOARD_SET_INDICATORS, ref KIPbuf, (UInt32)Marshal.SizeOf(KIPbuf), IntPtr.Zero, 0, ref bytesReturned, IntPtr.Zero))
        {
            Int32 err = Marshal.GetLastWin32Error();
            throw new Win32Exception(err);
        }

        // Console.WriteLine("Set new LED status");

        if(!CloseHandle(device))
        {
            Int32 err = Marshal.GetLastWin32Error();
            throw new Win32Exception(err);
        }

        // Console.WriteLine("Closed device handle");

        if(!DefineDosDevice(Flags.DDD_REMOVE_DEFINITION, "myKBD", null))
        {
            Int32 err = Marshal.GetLastWin32Error();
            throw new Win32Exception(err);
        }

        // Console.WriteLine("Removed device definition");
    }
};

class Flags
{
    public static IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);
    public const UInt32 IOCTL_KEYBOARD_SET_INDICATORS   = (0x0000000b << 16) | (0 << 14) | (0x0002 << 2) | 0; // from ntddkbd.h, ntddk.h
    public const UInt32 IOCTL_KEYBOARD_QUERY_INDICATORS = (0x0000000b << 16) | (0 << 14) | (0x0010 << 2) | 0; // from ntddkbd.h, ntddk.h

    public const UInt32 DDD_RAW_TARGET_PATH       = 0x00000001;
    public const UInt32 DDD_REMOVE_DEFINITION     = 0x00000002;
    public const UInt32 DDD_EXACT_MATCH_ON_REMOVE = 0x00000004;
    public const UInt32 DDD_NO_BROADCAST_SYSTEM   = 0x00000008;

    public const UInt32 GENERIC_ALL     = 0x10000000;
    public const UInt32 GENERIC_EXECUTE = 0x20000000;
    public const UInt32 GENERIC_WRITE   = 0x40000000;
    public const UInt32 GENERIC_READ    = 0x80000000;

    public const UInt32 CREATE_NEW        = 1;
    public const UInt32 CREATE_ALWAYS     = 2;
    public const UInt32 OPEN_EXISTING     = 3;
    public const UInt32 OPEN_ALWAYS       = 4;
    public const UInt32 TRUNCATE_EXISTING = 5;

    public const UInt16 KEYBOARD_SCROLL_LOCK_ON = 1;
    public const UInt16 KEYBOARD_NUM_LOCK_ON    = 2;
    public const UInt16 KEYBOARD_CAPS_LOCK_ON   = 4;
    public const UInt16 KEYBOARD_SHADOW         = 0x4000;
    public const UInt16 KEYBOARD_LED_INJECTED   = 0x8000;
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文