Mega Code Archive

 
Categories / Delphi / System
 

Windows hooks

Title: Windows hooks Question: A lot of articles has been written about windowsHooks. Here is yet an other one. The diffrence between this at all the others are that my hook implementation are done in a component, and your hook method are a method !!!! Answer: HOOKS A lot of articles has been written about windowsHooks. Here is yet an other one. The diffrence between this at all the others are that my hook implementation are done in a component, and your hook method are a method !!!! If you needd an introduction to HOOKS then I'm sure Dk3 can provide you one. This aim of this article are to provide the reader with a new way of implementing hooks. The Object orientated way. { ***************************************************************************** * * * Hooks * * * * By Jens Borrisholt * * Jens@Borrisholt.com * * * * This file may be distributed and/or modified under the terms of the GNU * * General Public License (GPL) version 2 as published by the Free Software * * Foundation. * * * * This file has no warranty and is used at the users own peril * * * * Please report any bugs to Jens@Borrisholt.com or contact me if you want * * to contribute to this unit. It will be deemed a breach of copyright if * * you publish any source code (modified or not) herein under your own name * * without the authors consent!!!!! * * * * CONTRIBUTIONS:- * * Jens Borrisholt (Jens@Borrisholt.com) [ORIGINAL AUTHOR] * * * ***************************************************************************** } unit hooks; interface uses Windows, Classes; type THookMsg = packed record Code: Integer; WParam: WPARAM; LParam: LPARAM; Result: LResult end; ULONG_PTR = ^DWORD; KBDLLHOOKSTRUCT = packed record vkCode, scanCodem, flags, time: DWORD; dwExtraInfo: ULONG_PTR; end; pKBDLLHOOKSTRUCT = ^KBDLLHOOKSTRUCT; const WH_KEYBOARD_LL = 13; WH_MOUSE_LL = 14; (* * Low level hook flags *) LLKHF_EXTENDED = $01; LLKHF_INJECTED = $10; LLKHF_ALTDOWN = $20; LLKHF_UP = $80; type THook = class; THookMethod = procedure(var HookMsg: THookMsg) of object; THookNotify = procedure(Hook: THook; var Hookmsg: THookMsg) of object; THook = class(TComponent) private fHook: hHook; fHookProc: Pointer; fOnPreExecute: THookNotify; fOnPostExecute: THookNotify; fActive: Boolean; fLoadedActive: Boolean; fThreadID: Integer; procedure SetActive(NewState: Boolean); procedure SetThreadID(NewID: Integer); procedure HookProc(var HookMsg: THookMsg); protected procedure PreExecute(var HookMsg: THookMsg; var Handled: Boolean); virtual; procedure PostExecute(var HookMsg: THookMsg); virtual; function AllocateHook: hHook; virtual; abstract; procedure Loaded; override; public constructor Create(Owner: TComponent); override; destructor Destroy; override; property ThreadID: Integer read fThreadID write SetThreadID stored False; property Active: Boolean read fActive write SetActive; property OnPreExecute: THookNotify read fOnPreExecute write fOnPreExecute; property OnPostExecute: THookNotify read fOnPostExecute write fOnPostExecute; published end; type TCallWndProcHook = class(THook) private protected public function AllocateHook: hHook; override; published property Active; property OnPreExecute; property OnPostExecute; end; type TCallWndProcRetHook = class(THook) private protected public function AllocateHook: hHook; override; published property Active; property OnPreExecute; property OnPostExecute; end; type TCBTHook = class(THook) private protected public function AllocateHook: hHook; override; published property Active; property OnPreExecute; property OnPostExecute; end; type TDebugHook = class(THook) private protected public function AllocateHook: hHook; override; published property Active; property OnPreExecute; property OnPostExecute; end; type TGetMessageHook = class(THook) private protected public function AllocateHook: hHook; override; published property Active; property OnPreExecute; property OnPostExecute; end; type TJournalPlaybackHook = class(THook) private protected public function AllocateHook: hHook; override; published property Active; property OnPreExecute; property OnPostExecute; end; type TJournalRecordHook = class(THook) private protected public function AllocateHook: hHook; override; published property Active; property OnPreExecute; property OnPostExecute; end; type TKeyboardHook = class(THook) private protected public function AllocateHook: hHook; override; published property Active; property OnPreExecute; property OnPostExecute; end; type TMouseHook = class(THook) private protected public function AllocateHook: hHook; override; published property Active; property OnPreExecute; property OnPostExecute; end; type TMsgHook = class(THook) private protected public function AllocateHook: hHook; override; published property Active; property OnPreExecute; property OnPostExecute; end; type TShellHook = class(THook) private protected public function AllocateHook: hHook; override; published property Active; property OnPreExecute; property OnPostExecute; end; type TSysMsgHook = class(THook) private protected public function AllocateHook: hHook; override; published property Active; property OnPreExecute; property OnPostExecute; end; type TLowLevelKeyboardHook = class(THook) private protected public function AllocateHook: hHook; override; published property Active; property OnPreExecute; property OnPostExecute; end; function MakeHookInstance(Method: THookMethod): Pointer; procedure FreeHookInstance(ObjectInstance: Pointer); implementation uses SysUtils; const InstanceCount = 313; // set so that sizeof (TInstanceBlock) type pObjectInstance = ^TObjectInstance; TObjectInstance = packed record Code: Byte; Offset: Integer; case Integer of 0: (Next: pObjectInstance); 1: (Method: THookMethod); end; pInstanceBlock = ^TInstanceBlock; TInstanceBlock = packed record Next: pInstanceBlock; Code: array[1..2] of Byte; WndProcPtr: Pointer; Instances: array[0..InstanceCount] of TObjectInstance; end; var InstBlockList: pInstanceBlock = nil; InstFreeList: pObjectInstance = nil; function StdHookProc(Code, WParam: WPARAM; LParam: LPARAM): LResult; stdcall; assembler; asm XOR EAX,EAX PUSH EAX PUSH LParam PUSH WParam PUSH Code MOV EDX,ESP MOV EAX,[ECX].Longint[4] CALL [ECX].Pointer ADD ESP,12 POP EAX end; { Allocate a hook method instance } function CalcJmpOffset(Src, Dest: Pointer): Longint; begin Result := Longint(Dest) - (Longint(Src) + 5); end; function MakeHookInstance(Method: THookMethod): Pointer; const BlockCode: array[1..2] of Byte = ($59, $E9); PageSize = 4096; var Block: pInstanceBlock; Instance: pObjectInstance; begin if InstFreeList = nil then begin Block := VirtualAlloc(nil, PageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); Block^.Next := InstBlockList; Move(BlockCode, Block^.Code, SizeOf(BlockCode)); Block^.WndProcPtr := Pointer(CalcJmpOffset(@Block^.Code[2], @StdHookProc)); Instance := @Block^.Instances; repeat Instance^.Code := $E8; Instance^.Offset := CalcJmpOffset(Instance, @Block^.Code); Instance^.Next := InstFreeList; InstFreeList := Instance; Inc(Longint(Instance), SizeOf(TObjectInstance)); until Longint(Instance) - Longint(Block) = SizeOf(TInstanceBlock); InstBlockList := Block end; Result := InstFreeList; Instance := InstFreeList; InstFreeList := Instance^.Next; Instance^.Method := Method end; { Free a hook method instance } procedure FreeHookInstance(ObjectInstance: Pointer); begin if ObjectInstance nil then begin pObjectInstance(ObjectInstance)^.Next := InstFreeList; InstFreeList := ObjectInstance end end; constructor THook.Create(Owner: TComponent); begin inherited Create(Owner); fHookProc := MakeHookInstance(HookProc); fActive := False; fLoadedActive := False; fHook := 0; ThreadID := GetCurrentThreadID; end; destructor THook.Destroy; begin Active := False; FreeHookInstance(fHookProc); inherited; end; procedure THook.SetActive(NewState: Boolean); begin if (csLoading in componentState) then begin fLoadedActive := NewState; end else if (fActive NewState) then begin fActive := NewState; case (Active and (not (csDesigning in ComponentState))) of True: begin fHook := AllocateHook; if (fHook = 0) then begin fActive := False; raise Exception.Create(Classname + ' CREATION FAILED!'); end; end; False: begin if (FHook 0) then UnhookWindowsHookEx(fHook); fHook := 0; end; end; end; end; procedure THook.SetThreadID(NewID: Integer); var IsActive: Boolean; begin IsActive := fActive; Active := False; fThreadID := NewID; Active := IsActive; end; procedure THook.Loaded; begin inherited; Active := fLoadedActive; end; procedure THook.HookProc(var HookMsg: THookMsg); var Handled: Boolean; begin Handled := False; PreExecute(HookMsg, Handled); if not Handled then begin with HookMsg do Result := CallNextHookEx(fHook, Code, wParam, lParam); PostExecute(HookMsg); end; end; procedure THook.PreExecute(var HookMsg: THookMsg; var Handled: Boolean); begin if Assigned(fOnPreExecute) then fOnPreExecute(Self, HookMsg); Handled := HookMsg.Result 0; end; procedure THook.PostExecute(var HookMsg: THookMsg); begin if Assigned(fOnPostExecute) then fOnPostExecute(Self, HookMsg); end; function TCallWndProcHook.AllocateHook: hHook; begin Result := SetWindowsHookEx(WH_CALLWNDPROC, fHookProc, HInstance, ThreadID); end; function TCallWndProcRetHook.AllocateHook: hHook; begin Result := SetWindowsHookEx(WH_CALLWNDPROCRET, fHookProc, hInstance, ThreadID); end; function TCBTHook.AllocateHook: hHook; begin Result := SetWindowsHookEx(WH_CBT, fHookProc, hInstance, ThreadID); end; function TDebugHook.AllocateHook: hHook; begin Result := SetWindowsHookEx(WH_DEBUG, fHookProc, hInstance, ThreadID); end; function TGetMessageHook.AllocateHook: hHook; begin Result := SetWindowsHookEx(WH_GETMESSAGE, fHookProc, hInstance, ThreadID); end; function TJournalPlaybackHook.AllocateHook: hHook; begin Result := SetWindowsHookEx(WH_JOURNALPLAYBACK, fHookProc, hInstance, ThreadID); end; function TJournalRecordHook.AllocateHook: hHook; begin Result := SetWindowsHookEx(WH_JOURNALRECORD, fHookProc, hInstance, ThreadID); end; function TKeyboardHook.AllocateHook: hHook; begin Result := SetWindowsHookEx(WH_KEYBOARD, fHookProc, hInstance, ThreadID); end; function TMouseHook.AllocateHook: hHook; begin Result := SetWindowsHookEx(WH_MOUSE, fHookProc, hInstance, ThreadID); end; function TMsgHook.AllocateHook: hHook; begin Result := SetWindowsHookEx(WH_MSGFILTER, fHookProc, hInstance, ThreadID); end; function TShellHook.AllocateHook: hHook; begin Result := SetWindowsHookEx(WH_SHELL, fHookProc, hInstance, ThreadID); end; function TSysMsgHook.AllocateHook: hHook; begin Result := SetWindowsHookEx(WH_SYSMSGFILTER, fHookProc, hInstance, ThreadID); end; function TLowLevelKeyboardHook.AllocateHook: hHook; begin Result := SetWindowsHookEx(WH_KEYBOARD_LL, fHookProc, hInstance, 0); end; end. An example of use : place two Tedit on a form and then the following code : uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, menus, Dialogs, Hooks, ComCtrls, clipBrd; type TForm1 = class(TForm) Edit1: TEdit; Edit2: TEdit; procedure FormCreate(Sender: TObject); private KeyboardHook: TKeyboardHook; procedure KeyboardHookPreExecute(Hook: THook; var Hookmsg: THookMsg); virtual; public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin KeyboardHook := TKeyboardHook.Create(Self); KeyboardHook.OnPreExecute := KeyboardHookPreExecute; KeyboardHook.Active := True; end; type TKeyState = (ksKeyDown, ksKeyIsDown, ksDummy, ksKeyUp); //ksDummy are NEVER to be used, it is only made as an place holder const LeftCtrlKey = $1D; RightCtrlKey = $1D + $100; LeftShiftKey = $2A; RightShiftKey = $36; AltAltGrKey = $38; AltKey = $2000 + AltAltGrKey; HoldingDown = $4000; GoingUp = $C000; procedure TForm1.KeyboardHookPreExecute(Hook: THook; var Hookmsg: THookMsg); var Key: Integer; KeyState : TKeyState; i: Integer; Shift, ShortCut: TShortCut; s : String; begin KeyState := TKeyState(Hookmsg.lParam shr 30); i := Hookmsg.lParam shr 16; if KeyState ksKeyDown then exit; Key := Hookmsg.wParam; if Key in [VK_NUMPAD0..VK_NUMPAD9] then Key := (Key - VK_NUMPAD0) + $30 else if Key = 188 then Key := VK_DECIMAL; Hookmsg.Result := 1; Shift := scNone; if GetKeyState(VK_SHIFT) Shift := Shift or scShift; if GetKeyState(VK_CONTROL) Shift := Shift or scCtrl; if GetKeyState(VK_MENU) Shift := Shift or scAlt; ShortCut := Key or Shift; Edit1.Text := IntToStr(ShortCut); Edit2.Text := ShortCutToText(ShortCut); Edit1.CopyToClipboard; s := cxTextEdit1.Text + '; //' + cxTextEdit2.Text; Clipboard.SetTextBuf(pointer(s)); end; Jens Borrisholt