2015-12-22 149 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

相關問題