当我的应用程序没有焦点时,如何捕获键盘状态?
How can I capture keyboard status when my application doesn't have the focus?
我女朋友的新笔记本电脑没有 NumLock 和 CapsLock 的 LED 指示灯,所以我写了一个小程序在屏幕上显示它们的状态:
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;
只要我的程序有焦点,这就有效,但当焦点转到另一个程序状态时,它就不再更新。 (但是,只需将鼠标移到表格上,无需单击即可更新。)
如何在另一个应用程序获得焦点时让程序更新?
您应该使用 Low Level Keyboard Hook,因为这样即使您的应用程序没有焦点,您也可以在每次击键时得到通知。
我为你创建了一个小例子
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 = [=10=]80;
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.
基本上在 formCreate
上,我挂接键盘,并告诉我的程序我需要在哪个函数中收到通知。在我的示例中,我将其称为 LowLevelKeyboardProc
然后你只需要测试按下的是哪个键,如果是CapsLock或Num lock中的一个则通知表单。
您只需在计时器中使用 GetKeyState
。
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;
即使您的应用程序没有焦点,这也能正常工作。
在 XP 上测试。
我女朋友的新笔记本电脑没有 NumLock 和 CapsLock 的 LED 指示灯,所以我写了一个小程序在屏幕上显示它们的状态:
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;
只要我的程序有焦点,这就有效,但当焦点转到另一个程序状态时,它就不再更新。 (但是,只需将鼠标移到表格上,无需单击即可更新。)
如何在另一个应用程序获得焦点时让程序更新?
您应该使用 Low Level Keyboard Hook,因为这样即使您的应用程序没有焦点,您也可以在每次击键时得到通知。
我为你创建了一个小例子
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 = [=10=]80;
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.
基本上在 formCreate
上,我挂接键盘,并告诉我的程序我需要在哪个函数中收到通知。在我的示例中,我将其称为 LowLevelKeyboardProc
然后你只需要测试按下的是哪个键,如果是CapsLock或Num lock中的一个则通知表单。
您只需在计时器中使用 GetKeyState
。
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;
即使您的应用程序没有焦点,这也能正常工作。 在 XP 上测试。