当我的应用程序没有焦点时,如何捕获键盘状态?

时间:2015-12-22 07:01:00

标签: delphi delphi-xe8

我女朋友的新笔记本电脑没有NumLock和CapsLock的指示灯,所以我写了一个小程序,在屏幕上显示其状态:

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

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

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

2 个答案:

答案 0 :(得分:8)

您只需在计时器中使用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 :(得分:4)

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

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