Initial Problem:
I use an external keyboard at the office, so I want the NumLock ON. But when I'm at home I just use the laptop keyboard, so then I get numbers instead of letters and I have to turn NumLock OFF.
Initial Solution: The below script detects one or two keyboards and turns NumLock ON or OFF as appropriate.
New Problem:
This works perfectly from the command line, but I want it to trigger when I log in and happen automatically. When I run it from Task Scheduler in the background, this line doesn't work:
Shell.SendKeys "{NUMLOCK}"
It fires but doesn't toggle the lock. No errors reported.
UPDATE: If I schedule it to run under my account "only when user is logged on" then it works but shows the cmd window. If I run it under my account or under the SYSTEM account with "whether user is logged in or not" the window goes away nicely but it doesn't work.
Whether from cmd or run as a scheduled task, I get this output when it should toggle the lock:
Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.
Found HID Keyboard Device
Found HID Keyboard Device
numLock is OFF
Toggling Numlock
So the script itself is working correctly.
UPDATE2: Looks like it might have something to do with not having a windows station when running as a background task. Turns out that DetectNumlockConsole.exe
isn't working either. That is a simple c# app that returns the results of this line
numLock = Control.IsKeyLocked(Keys.NumLock);
Again, this works when run "only when user is logged on" but not "whether user is logged in or not."
--------- vbs script -----------
set OUT = WScript.StdOut
Set Shell=CreateObject("Wscript.Shell")
Dim KeyCount
KeyCount = 0
Computer = "."
'set NumLock = CheckState
Set WMIService = GetObject("winmgmts:\\" & Computer & "\root\cimv2")
Set Devices = WMIService.ExecQuery ("Select * From Win32_USBControllerDevice")
For Each Device in Devices
DeviceName = Device.Dependent
Quotes = Chr(34)
DeviceName = Replace(DeviceName, Quotes, "")
DeviceNames = Split(DeviceName, "=")
DeviceName = DeviceNames(1)
Set USBDevices = WMIService.ExecQuery ("Select * From Win32_PnPEntity Where DeviceID = '" & DeviceName & "'")
For Each USBDevice in USBDevices
'OUT.WriteLine USBDevice.Description ' Write description to command line to see what to look for
If InStr( LCase( USBDevice.Description ), "keyboard" ) <> 0 Then
KeyCount = KeyCount + 1
OUT.WriteLine "Found " & USBDevice.Description
End If
Next
Next
dim numLock
numLock = Shell.Run("DetectNumlockConsole.exe",0,True)
If (numLock = 0) Then
OUT.WriteLine "numLock is OFF"
Else
OUT.WriteLine "numLock is ON"
End If
' If we have a keyboard, and numlock is OFF
' Or we don't have a keyboard, and numlock is ON
' Then toggle it
If (((KeyCount > 1) AND (numLock = 0)) OR ((KeyCount = 1) AND (numLock = 1))) Then
Shell.SendKeys "{NUMLOCK}" ' *** Problem here, doesn't toggle **
OUT.WriteLine "Toggling Numlock"
End If
That is how windows security works. It is nothing to do with sendkeys per se, but tasks under different security contexts cannot affect other tasks.
As you can see it works when run under the same security context as only run when user logged in does.
It's called process isolation and the principal is that no one can mess with the interactive user for both security and UI principals.