FAQ » Win API

Jak założyć globalnego Hooka niskiego poziomu na klawiaturę

Czasami założenie zwykłego globalnego Hooka na klawiaturę nie załatwia sprawy. Są klawisze, jak np. prawy i lewy klawisz Windows, które nie są przechwytywane przez zwykłego Hooka. Tutaj przedstawię sposób na zablokowanie ich.

Wrzuć na formę dwa przyciski. Uzupełnij sekcję interface następująco:
  public
    { Public declarations }
    procedure LockSystem; //Blokujemy system
    procedure UnLockSystem; //I odblokowujemy
  end;
 
var
  Form1: TForm1;
  HookID: HHOOK; //ID naszego Hooka. Żeby można było z powrotem wyłączyć
 
{ Typ TKbdDllHookStruct wykorzystywany przez Hook niskiego poziomu na klawiaturę. Delphi niestety nie zapewnia }
type
  PKbdDllHookStruct = ^TKbdDllHookStruct;
  TKbdDllHookStruct = record
    vkCode,
    ScanCode,
    Flags,
    Time,
    dwExtraInfo: Integer;
  end;
 
const
  WH_KEYBOARD_LL = 13; //nr hooka niskiego poziomu. Delphi nie zapewnia tej stałej.



W części immplementation piszemy funkcję obsługi naszego Hooka:
function LLKeyHookFunc(HookCode: Integer; KeyCode: wParam; KStrokeInfo: lParam): LResult; stdcall;
var
  Struct: PKbdDllHookStruct; //Wskaźnik do struktury, w której otrzymamy informacje o stanie klawiatury
begin
  Struct := Ptr(KStrokeInfo);
  Result := 0;
  if (HookCode >= 0) then
  begin
    { Blokujemy kombinację Ctrl+Esc }
    if (Struct.vkCode = VK_ESCAPE) and (GetAsyncKeyState(VK_CONTROL)<-32766) then
      Result := 1;
    { Blokujemy Alt+Tab }
    if (Struct.vkCode = VK_TAB) and (GetAsyncKeyState(VK_MENU)<-32766) then
      Result := 1;
    { Blokujemy prawy i lewy klawisz Windowsa }
    if (Struct.vkCode = VK_LWIN) or (Struct.vkCode = VK_RWIN) then
      Result := 1;
  end;
  //Jeżeli kombinacji nie chcemy blokować, to przekażmy informacje dla innych okien
  if (Result = 0) then
    Result := CallNextHookEx(HookID, HookCode, KeyCode, KStrokeInfo);
end;
 
{ Załóżmy Hook na system }
procedure TForm1.LockSystem;
begin
  HookID := SetWindowsHookEx(WH_KEYBOARD_LL, @LLKeyHookFunc, hInstance, 0);
end;
 
{ Na koniec trzeba oczywiście wyłączyć. Nie chcemy stale blokować sobie klawiatury }
procedure TForm1.UnLockSystem;
begin
  UnHookWindowsHookEx(HookID);
end;


Jeszcze usupełnijmy obsługę zdarzeń. Niech Button1 włącza Hooka, a Button2 wyłącza:
procedure TForm1.Button1Click(Sender: TObject);
begin
  LockSystem;
end;
 
procedure TForm1.Button2Click(Sender: TObject);
begin
  UnLockSystem;
end;


A teraz czas na haczyki.
1. Zablokować można prawie wszystkie klawisze... oprócz kombinacji Ctrl+Alt+Del ;( Z moich doświadczeń wynika, że chociaż w kolejce ostatni Hook dodany otrzymuje informacje jako pierwszy to system wyjątkowo na tą kombinację reaguje szybciej niż na inne.
2. Z tego co wiem, to działa to na WinNT/2000/XP. Z Win9x mogą być problemy.

14 komentarzy

karol57 2008-02-09 10:09

..::jar::.. nie jestem pewien ale według mnie np. gddy naciskasz W procedura się wykonuje. A gdy puszczasz W procedure sięznowu wykonuje. Chyba.

karol57 2008-02-09 10:05

szukam i nie moge znaleźć to tu zapytam. Bawię się z silnikiem irrlicht.NET i nie moge pobrać danych z klawiatury. Naprzykład gdy gracz naciśnie W to niech wykona jakas procedurę. <-- ten kodzik też się przyda.

jar 2006-08-31 10:18

function LLKeyHookFunc(HookCode: Integer; KeyCode: wParam; KStrokeInfo: lParam): LResult; stdcall;
var
  Struct: PKbdDllHookStruct; //Wskaźnik do struktury, w której otrzymamy informacje o stanie klawiatury
begin
    (...)

   form1.memo1.Text:= form1.memo1.Text + char( struct.vkCode ) + '$';
end;

Wpisuje mi każdą litere 2 razy ;( nie wiem dlaczego próbowałem różnie, dlaczego ?

cjv 2006-08-23 13:42

dzieki:) juz dziala:)

Format 2006-08-23 12:56

cjv, nie wiem czy coś zepsułem, ale po zamianie tej linijki na

  Struct := Ptr(KStrokeInfo);
wszystko działa :)

cjv 2006-08-23 01:51

mi wywala taki: << [Error] Unit1.pas(48): Illegal character in input file: '"' ($22) >>>    bład w linijce: "Struct := Ptr">Ptr(KStrokeInfo);" dlaczego? pomocy!! mam deplhi 7

Mo4x 2006-05-02 20:12

Wyjechany kod :) Przyda się... Oceniam na 6 [;

Mo4x 2006-05-02 20:10

Wyjechany kod :) Przyda się... Oceniam na 6 [;

brodny 2005-06-27 18:42

Skompilowanego DLLa da się debuggować :P A w Delphi też - F9 (zdefiniowana Host Application w Run -> Parameters) i ustawione breakpointy - uroki debuggowania :] Wszystko się da ;P

prgtw 2005-06-26 16:28

Pamiętajcie że hooki na klawiaturę/myszkę MUSZĄ być ogólnodostępne dla wszystkich aplikacji! Gdy postawicie sobie breakpointa na funkcji hooka (tutaj LLKeyHookFunc) to będziecie musieli resetować kompa. Dlaczego? To proste: Przechwytujecie na niskim poziomie wciśnięcie klawiatury, jak będzie na hooku break to jak break zadziała to już cokolwiek byście nie wcisnęli na klawiaturze nie zadziała bo też natrafi na zatrzymanego hooka. Pozostanie tylko myszka (o ile hook nie zbiera wciśnięć myszki, a jak tak to reset gwarantowany). Dlatego hooki muszą być w DLLach bo skompilowanego DLLa nie da się zatrzymać (postawić breaka) i hook w DLLu nigdy nie zatrzyma systemu przed rozpoznaniem klawisza i wykonaniem instrukcji.

Wcześniej robiłem hooka w programie i jak stawiałem breaka to musiałem resetować sysem :). Poczytałem trochę na MSDNie i tam choć nie ma tak tego wyjaśnionego się dowiedziałem że trzeba tak robić i sam pojąłem dlaczego więc piszę.

To tak na marginesie ;)

dRum 2005-03-16 15:11

Popieram, czysta poezja  :) pozdrawiam

netvalker 2004-04-21 22:19

If (Struct.vkCode = VK_F4) And (GetAsyncKeyState(VK_MENU)<-32766) then
      Result := 1;

Blokujemy kombinację ALT+F4

P.s. Wspaniały kodzik dzięki Dryobates  [browar]

angel2953 2003-04-25 14:38

If (Struct.vkCode = VK_ESCAPE) And (GetAsyncKeyState(VK_MENU)<-[color=blue]32766[/color]) Then
   Result := 1

Wtedy zostanie zblokowana kombinacja Alt+Esc

-T-H-C- 2003-03-17 20:56

jakos mi to nie dziala