简体   繁体   中英

Toggle NUMLOCK / CAPSLOCK / SCROLLLOCK while the workstation is locked?

I am trying to toggle the indicator lights on my keyboard for Num Lock, Caps Lock, and Scroll Lock. (I just want to have them turn them off automatically at nighttime.) This is trivial using AutoHotkey or AutoIt. However, the script does has not effect if the workstation is locked.

While researching doing this will some kind of DLL call, I came across SetKeyboardState in user32.dll . Unfortunately, according to the Windows Dev Center documentation, it is not possible to set the keyboard state of these three keys (and they are each mentioned by name) using SetKeyboardState .

https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-setkeyboardstate


Another thread said to simply use ControlSend in AutoIt. That is typically very reliable, even when there is no active GUI session, or the keyboard/mouse are being used interactively.

The following works great:

ControlSend("", "", "", "{NUMLOCK off}")

... but only when there is an active GUI session. This has no effect when the screen is locked.


I know these are special keys: instead of controlling the input state of the software thread, they are part of the physical keyboard state and control global input for the entire system.

Are there any options at all? In any language?

At least on Windows, it really seems there is no way to toggle these keys when the workstation is locked. No matter what language or framework is in use, they all need to pass through the underlying OS layer. Without an interactive session, these key presses are not sent.

Naturally, if you are standing at the keyboard and pressing these keys, they respond just fine.

So, this CAN be done, by controlling a keyboard as an attached peripheral, such as Arduino. Some of these models can act as a USB keyboard/mouse. (I have tried this using both the Arduino Leonardo and Spark Fun Pro Micro. Both respond the same way for this use case.)

NOTE: even when toggle keys are updated by Arduino or by a person at the terminal, the toggle key states are NOT updated in the operating system until the workstation is unlocked. Whatever state a toggle key has when the workstation is locked, regardless of what the person at the locked terminal does with the keyboard, that key will continue to appear in that state to any running scripts until the moment the workstation is unlocked. This can be easily verified using the AHK script below.


Below is a minimal example of the AutoHotKey control script (although any program that can send data over a serial connection will do just as well) , and the Arduino sketch:

AutoHotKey control script

Loop
{
  RS232_FileHandle := RS232_Initialize()
  if (RS232_FileHandle)
  {
    ; Turn them all off
    (1 = GetKeyState("NumLock", "T")) ? RS232_Write(RS232_FileHandle, "219") : NA
    Sleep, 750
    (1 = GetKeyState("CapsLock", "T")) ? RS232_Write(RS232_FileHandle, "193") : NA
    Sleep, 750
    (1 = GetKeyState("ScrollLock", "T")) ? RS232_Write(RS232_FileHandle, "207") : NA

    Sleep, 4000

    ; Turn them all on
    (0 = GetKeyState("NumLock", "T")) ? RS232_Write(RS232_FileHandle, "219") : NA
    Sleep, 750
    (0 = GetKeyState("CapsLock", "T")) ? RS232_Write(RS232_FileHandle, "193") : NA
    Sleep, 750
    (0 = GetKeyState("ScrollLock", "T")) ? RS232_Write(RS232_FileHandle, "207") : NA

    RS232_Close(RS232_FileHandle)
  }
  Sleep, 4000
}

RS232_LoadSettings()
{
  RS232_Port     := "COM3"
  RS232_Baud     := "9600"
  RS232_Parity   := "N"
  RS232_DataBits := "8"
  RS232_StopBits := "1"
  RS232_Timeout  := "Off"
  RS232_XonXoff  := "Off"
  RS232_CTS_Hand := "Off"
  RS232_DSR_Hand := "Off"
  RS232_DSR_Sens := "Off"
  RS232_DTR      := "Off"
  RS232_RTS      := "Off"

  ; MSDN Reference: https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/mode
  RS232_Settings = %RS232_Port%:BAUD=%RS232_Baud% PARITY=%RS232_Parity% DATA=%RS232_DataBits% STOP=%RS232_StopBits% to=%RS232_Timeout% xon=%RS232_XonXoff% odsr=%RS232_DSR_Hand% octs=%RS232_CTS_Hand% dtr=%RS232_DTR% rts=%RS232_RTS% idsr=%RS232_DSR_Sens%
  return RS232_Settings
}

RS232_Initialize()
{
  ; Source adapted from: https://autohotkey.com/board/topic/26231-serial-com-port-console-script/
  RS232_Settings := RS232_LoadSettings()
  RS232_Port     := StrSplit(RS232_Settings, ":")[1]
  RS232_COM      := (4 <= StrLen(RS232_Port) ? "\\.\" : "") . RS232_Port
  StringTrimLeft, RS232_Settings, RS232_Settings, StrLen(RS232_Port)+1
  VarSetCapacity(DCB, 28)
  if (1 <> DllCall("BuildCommDCB","str",RS232_Settings,"UInt",&DCB))
  {
    return false
  }
  hCom := DllCall("CreateFile","Str",RS232_COM,"UInt",0xC0000000,"UInt",3,"UInt",0,"UInt",3,"UInt",0,"UInt",0,"Cdecl Int")
  if (hCom < 1)
  {
    return false
  }
  if (1 <> DllCall("SetCommState","UInt",hCom,"UInt",&DCB))
  {
    RS232_Close(hCom)
    return false
  }
  VarSetCapacity(Data, 20, 0)
  NumPut(0xffffffff, Data,  0, "UInt")
  NumPut(0x00000000, Data,  4, "UInt")
  NumPut(0x00000000, Data,  8, "UInt")
  NumPut(0x00000000, Data, 12, "UInt")
  NumPut(0x00000000, Data, 16, "UInt")
  if (1 <> DllCall("SetCommTimeouts","UInt",hCom,"UInt",&Data))
  {
    RS232_Close(hCom)
    return false
  }
  return hCom
}

RS232_Write(hCom, msg)
{
  SetFormat, Integer, DEC
  StringSplit, Byte, msg, `,
  Data_Length := Byte0
  VarSetCapacity(Data, Byte0, 0xFF)
  i := 1
  Loop %Byte0%
  {
    NumPut(Byte%i%, Data, (i-1) , "UChar")
    i++
  }

  Bytes_Sent := 0
  WF_Result := DllCall("WriteFile","UInt",hCom,"UInt",&Data,"UInt",Data_Length,"UInt*",Bytes_Sent,"Int","NULL")
  if (WF_Result <> 1 or Bytes_Sent <> Data_Length)
  {
    return false
  }
  return Bytes_Sent
}

RS232_Close(hCom)
{
  return (1 == DllCall("CloseHandle","UInt",hCom))
}

Arduino sketch

/* Pro Micro NumCapsScrollToggleDemo
   by: Jonathan David Arndt
   date: March 6, 2020

   This will allow the toggle of the Num Lock, Caps Lock, and Scroll Lock keys
   on the keyboard, via commands sent over USB serial
*/

#include <Keyboard.h>

// You could patch this into your Keyboard.h file, or just define it here
// Source: https://forum.arduino.cc/index.php?topic=173583.0 (attachment: USBAPI.h)
#define KEY_NUM_LOCK    0xDB
#define KEY_SCROLL_LOCK 0xCF

void pressAndRelease(int c);

void setup()
{
  Serial.begin(9600); // This pipes to the serial monitor
  delay(3000);        // Wait a moment for things to get setup
  Serial.println("Initialize Serial Monitor");
}

void loop()
{
  int c = 0;

  if (0 < Serial.available())
  {
    c = Serial.read();
    if (219 == c)
    {
      pressAndRelease(KEY_NUM_LOCK);
    }
    else if (193 == c)
    {
      pressAndRelease(KEY_CAPS_LOCK);
    }
    else if (207 == c)
    {
      pressAndRelease(KEY_SCROLL_LOCK);
    }
  }
}

void pressAndRelease(int c)
{
  Keyboard.press(c);
  Keyboard.release(c);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM