My girlfriend's new laptop doesn't have indicator LEDs for NumLock and CapsLock, so I wrote a small program which show their status on screen:
procedure TForm1.Timer1Timer(Sender: TObject);
var
KeyState: TKeyboardState;
begin
GetKeyboardState(KeyState);
if KeyState[VK_NUMLOCK] = 0 then
PanelNumLock.Color := clSilver
else
PanelNumLock.Color := clLime;
if KeyState[VK_CAPITAL] = 0 then
PanelCapsLock.Color := clSilver
else
PanelCapsLock.Color := clLime;
end;
This works as long as my program has the focus, but when the focus goes to anther program statuses are no longer updated. (However, just moving the mouse over the form, no clicking, is sufficient for updating.)
How can I let the program update when another application has the focus?
You can simply use GetKeyState
in your Timer.
if GetKeyState(VK_NUMLOCK) = 1 then
PanelNumLock.Color := clLime
else
PanelNumLock.Color := clSilver;
if GetKeyState(VK_CAPITAL) = 1 then
PanelCapsLock.Color := clLime
else
PanelCapsLock.Color := clSilver;
This works even when your application doesn't have the focus. Tested on XP.
You should use a Low Level Keyboard Hook, because then you can get notified for every keystroke even if you application does not have focus.
I've create a small example for you
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Vcl.ExtCtrls;
const
WM_UpdateScreen = WM_USER + 1;
type
TForm1 = class(TForm)
PanelCapsLock: TPanel;
PanelNumlock: TPanel;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
FHook: hHook;
KeyState: TKeyboardState;
public
procedure UpdateScreen(var message: TMessage); message WM_UpdateScreen;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
type
pKBDLLHOOKSTRUCT = ^KBDLLHOOKSTRUCT;
KBDLLHOOKSTRUCT = packed record
vkCode: DWORD;
scanCodem: DWORD;
flags: DWORD;
time: DWORD;
dwExtraInfo: ULONG_PTR;
end;
var
hkHook: hHook;
function LowLevelKeyboardProc(code: Integer; WParam: WParam; LParam: LParam): LRESULT stdcall;
const
LLKHF_UP = $0080;
var
Hook: pKBDLLHOOKSTRUCT;
bControlKeyDown: Boolean;
begin
try
Hook := pKBDLLHOOKSTRUCT(LParam);
case code of
HC_ACTION:
begin
if (Hook^.flags and LLKHF_UP) <> 0 then
if Hook.vkCode in [VK_NUMLOCK, VK_CAPITAL] then
PostMessage(Form1.Handle, WM_UpdateScreen, Hook.vkCode, 0);
end;
end;
finally
Result := CallNextHookEx(hkHook, code, WParam, LParam);
end;
end;
procedure HookIt;
begin
hkHook := SetWindowsHookEx(WH_KEYBOARD_LL, @LowLevelKeyboardProc, hInstance, 0);
end;
procedure UnHookIt;
begin
UnHookWindowsHookEx(hkHook);
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
UnHookIt;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
GetKeyboardState(KeyState);
PostMessage(Handle, WM_UpdateScreen, VK_CAPITAL, 1);
PostMessage(Handle, WM_UpdateScreen, VK_NUMLOCK, 1);
HookIt;
end;
procedure TForm1.UpdateScreen(var message: TMessage);
begin
if message.LParam = 0 then
if KeyState[message.WParam] = 0 then
KeyState[message.WParam] := 1
else
KeyState[message.WParam] := 0;
if KeyState[VK_NUMLOCK] = 0 then
PanelNumlock.Color := clSilver
else
PanelNumlock.Color := clLime;
if KeyState[VK_CAPITAL] = 0 then
PanelCapsLock.Color := clSilver
else
PanelCapsLock.Color := clLime;
end;
end.
Basically on formCreate
I hook the Keyboard, and tells my program in which function I need my notification. In my example I called it LowLevelKeyboardProc
Then you just need to test fot which key is pressed and if it is one of CapsLock of Num lock then nofify the form.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With