2015-12-22 60 views
8

我女朋友的新的笔记本电脑不具备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; 

enter image description here

这工作只要我的程序有重点,但是当重点转向其他程序状态不再更新时。 (但是,只需将鼠标移到表单上,不需要单击就足以进行更新。)

如何在另一个应用程序具有焦点时让程序更新?

+0

操作系统? –

+0

@JasonC Windows 7 –

+0

您的最后一段已经提出了一种可能的解决方案:使用'GetCursorPos'和'SendMessage(...,WM_MOUSEMOVE,...)'强制消息循环更新应用程序中当前的键盘状态在背景中。 – mghie

回答

8

您可以在Timer中简单使用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上测试。

+1

也在Win 7下工作。谢谢一堆。 (我听到有关Win 10的很糟糕的事情,但我可以衷心地推荐从XP升级到Win 7.它将支持到2020年。) –

+1

只是为了记录这两个解决方案在Windows 10下工作 –

3

您应该使用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 = $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. 

基本上在formCreate我勾了键盘,并告诉我的程序在运作,我需要我的通知。在我的例子中,我叫它LowLevelKeyboardProc

然后,你只需要测试fot哪个键被按下,如果它是Num Lock的CapsLock之一,然后nofify表单。

+0

这不符合预期。您的挂钩帖子WM_UpdateScreen消息。然后调用GetKeyboardState。如果表单不集中,状态将不会被反映(回到OP最初的问题)。在XP上测试。 – kobik

+0

它在我的电脑上做过 –

+3

我看不出它是如何做到的。计时器做了同样的事情。问题是在未聚焦的窗口上的GetKeyboardState。 – kobik

相关问题