Delphi - Billentyű lenyomások számlálása

forráskód letöltése
Tudja Ön, hogy hány billentyűt üt le egy nap alatt a különböző alkalmazások használata közben?
Készítünk egy alkalmazást, mely képes arra, hogy bármilyen program futása közben számlálja a billentyű lenyomásokat. Teszi ezt úgy, hogy a számláló minden nap nulláról újra indul, így a program használatával a fenti kérdésre megkaphatja a választ.
A megvalósításhoz szükséges az, hogy bármely futó alkalmazásban történt billentyű lenyomásról értesüljön a saját alkalmazásunk. Ezt csak úgy tudjuk megvalósítani, hogy kihasználjuk a Windows által nyújtott ún. billentyű hook szolgáltatást. Ezt felhasználva programunk minden billentyű lenyomásról és felengedésről értesítést kap.
A hook használatához kell készítenünk egy DLL-t. Ez a mellékelt KeyHook.dpr projekt lesz. Ebben két függvényt kell exportálnunk SetHookKey illetve UnHookKey néven.
function SetHookKey: boolean; export;
function UnHookKey: boolean; export;
A számláló aktuális értékét a Windows regisztrációs adatbázisban tároljuk az alábbi kulcson:
  key='Software\Animare Software\KeyMouse\';
Amikor a SetHookKey függvényt meghívjuk, akkor kell megkérnünk a Windows-t arra, hogy értesítse alkalmazásunkat minden olyan esetben, amikor egy-egy billentyű lenyomásra illetve felengedésre került. Ehhez a SetWindowsHookEx függvényt használjuk, melynek első paraméterében a billentyűzettel kapcsolatos információkat kérjük. Második paraméterben adjuk meg annak a függvénynek a nevét, melyet a billentyű lenyomásakor meg kell hívnia a Windows-nak.
function SetHookKey: Boolean;
begin
  if not FOk then begin
    FH:=SetWindowsHookEx(WH_KEYBOARD, HookProc, integer(HInstance), 0);
    FOk:=true;
  end;
  result:=FH<>0;
end;
Amikor már nincs szükségünk erre a szolgáltatásra, akkor kell meghívnunk az UnHookKey függvényt. Ezen belül az UnHookWindowsHookEx függvényt használjuk. Ennek egyetlen paramétert kell megadnunk, mégpedig azt a számot, melyet a SetWindowsHookEx függvény adott vissza.
function UnHookKey:Boolean;
begin
  result:=UnHookWindowsHookEx(FH);
  FOk:=false;
end;
Nézzük, mi történik akkor, amikor a billentyű lenyomásra kerül. Bármely program legyen az aktív, a Windows meghívja a HookProc nevű függvényünket. Mivel ez megtörténik abban az esetben is, amikor valamelyik billentyű felengedésre kerül, így az egyiket ki kell szűrnünk annak érdekében, hogy egy billentyű lenyomás csak eggyel növelje a számlálónk aktuális értékét.
function HookProc(Code: integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
  if (Code=HC_ACTION) and (lParam and $80000000 = $80000000) then begin
Ha ez megtörtént, akkor növelhetjük a számlálónk értékét, mely a fent megadott Windows regisztrációs adatbázis kulcson található, melynek neve az aktuális dátum alapján kerül meghatározásra, mivel minden nap új számlálóra van szükségünk. A számláló értékének eggyel történő növelése után a kapott értéket visszaírjuk a registry-be.
    s:=FormatDateTime('yyyymmdd', Now);
    reg:=TRegistry.Create;
    reg.OpenKey(key+s, true);
    try
      i:=reg.ReadInteger('Key');
    except
      i:=0;
    end;
    reg.WriteInteger('Key', i+1);
    reg.Free;
  end;
Függvényünk utolsó lépéseként a CallNextHookEx Windows függvényt kell meghívnunk, mely aktivizálja az esetlegesen más alkalmazások által hasonló módon igényelt hook szolgáltatást.
  result:=CallNextHookEx(FH, Code, wParam, lParam);
end;
Ezzel a DLL-ünk elkészült, most nézzük a felhasználását.
A Project1.dpr alkalmazásban használjuk fel ezt a DLL-t. Az abban exportált két függvényt deklaráljuk külsőként.
function SetHookKey: boolean; far; external 'KeyHook';
function UnHookKey: boolean; far; external 'KeyHook';
A programban egy olyan Form-ot készítünk, melynek nincs fejléce, és csupán egy Label komponens helyezkedik el rajta. Beállítjuk úgy a Form-ot, hogy mindig minden alkalmazás felett látható legyen. Így folyamatosan tájékoztatást kaphatunk arról, hogy aktuálisan hány billentyűt nyomtunk le az adott napon. Hogy a folyamatosság fenntartható legyen egy Timer komponenst alkalmazunk. Amikor ennek OnTimer eseménye létrejön, akkor kiolvassuk a megfelelő registry kulcsról az ott tárolt billentyű lenyomások számát és megjelenítjük a Label komponensen.
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  s:=FormatDateTime('yyyymmdd', Now);
  reg:=TRegistry.Create;
  reg.OpenKey(key+s, true);
  try
    i:=reg.ReadInteger('Key');
  except
    i:=0;
  end;
  Label1.Caption:=IntToStr(i);
  reg.Free;
end;
Amikor a programunk elindul, akkor gondoskodunk arról, hogy a tálcán ne jelenjen meg hozzá nyomógomb, valamint aktivizáljuk a DLL-t a SetHookKey függvény hívásával, és ezzel elindítjuk a számlálási folyamatot.
procedure TForm1.FormCreate(Sender: TObject);
begin
  SetWindowLong(Application.Handle, GWL_EXSTYLE, GetWindowLong(Application.Handle, GWL_EXSTYLE) or WS_EX_TOOLWINDOW and not WS_EX_APPWINDOW);
  SetHookKey;
end;
Amikor az alkalmazásunk futása véget ér, akkor hívjuk meg az UnHookKey függvényt, mely leállítja a számlálást.
procedure TForm1.FormDestroy(Sender: TObject);
begin
  UnHookKey;
end;
Ezzel alkalmazásunk elkészült, melyet célszerű elhelyeznünk a Windows indítópultján, így gépünk indulásakor automatikusan indul a számlálás is.