DelphiFAQ Home Search:

How can I create a system wide keyboard hook under Win32?

 

comments7 comments. Current rating: 5 stars (3 votes). Leave comments and/ or rate it.

I found the following code posted in a newsgroup. Since it is asked frequently, I add it here.
Comments:

The following example demonstrates creating a system wide windows hook under Win32. The example provides both the code for the system hook dll and an example application. The hook function that we will create will also demonstrate advanced coding techniques such as sharing global memory across process boundaries using memory mapped files, sending messages from the key hook function back to the originating application, and dynamic loading of a dll at runtime.

The example keyboard hook that we create will keep a count of the number of keystrokes a user enters on the keyboard. Further, we will demonstrate trapping the enter key, and passing a message back to the application that initiated the keyboard hook each time the enter key is pressed. Finally, we will demonstrate trapping the left arrow key and instead of letting it through to the current application, we will instead replace it with a right arrow keystroke. (Note: that this can
cause much confusion to a unsuspecting user).

Library TheHook;

uses
  Windows, Messages, SysUtils;

{Define a record for recording and passing information process wide}
type
  PHookRec = ^ THookRec;
  THookRec = Packed Record
    TheHookHandle: HHOOK;
    TheAppWinHandle: HWnd;
    TheCtrlWinHandle: HWnd;
    TheKeyCount: DWord;
  end;

var
  hObjHandle : THandle; {Variable for the file mapping object}
  lpHookRec  : PHookRec;
{Pointer to our hook record}
procedure MapFileMemory (dwAllocSize: DWord);
begin { MapFileMemory }
  {Create a process wide memory mapped variable}
  hObjHandle := CreateFileMapping ($FFFFFFFF, Nil, PAGE_READWRITE, 0,
    dwAllocSize, 'HookRecMemBlock');
  if (hObjHandle = 0) then
    begin
      MessageBox (0, 'Hook DLL', 'Could not create file map object', mb_Ok);
      exit
    end { (hObjHandle = 0) };
  {Get a pointer to our process wide memory mapped variable}
  lpHookRec := MapViewOfFile (hObjHandle, FILE_MAP_WRITE, 0, 0, dwAllocSize);
  if (lpHookRec = Nil) then
    begin
      CloseHandle (hObjHandle);
      MessageBox (0, 'Hook DLL', 'Could not map file', mb_Ok);
      exit
    end { (lpHookRec = Nil) }
end; { MapFileMemory }


procedure UnMapFileMemory;
begin { UnMapFileMemory }
  {Delete our process wide memory mapped variable}
  if (lpHookRec <> Nil) then
    begin
      UnMapViewOfFile (lpHookRec);
      lpHookRec := Nil
    end { (lpHookRec <> Nil) };
  if (hObjHandle > 0) then
    begin
      CloseHandle (hObjHandle);
      hObjHandle := 0
    end { (hObjHandle > 0) }
end; { UnMapFileMemory }


function GetHookRecPointer : pointer
  stdcall;
begin { GetHookRecPointer }
  {Return a pointer to our process wide memory mapped variable}
  Result := lpHookRec
end; { GetHookRecPointer }


{The function that actually processes the keystrokes for our hook}
function KeyBoardProc (code: Integer; wParam: Integer; lParam: Integer) :
  Integer;
  stdcall;
var
  KeyUp : bool;
{Remove comments for additional functionability
  IsAltPressed : bool;
  IsCtrlPressed : bool;
  IsShiftPressed : bool;
 } 
begin { KeyBoardProc } 
  Result := 0; 
  
  Case code Of 
  HC_ACTION: 
    begin 
      {We trap the keystrokes here} 
      {Is this a key up message?} 
      KeyUp := ((lParam and (1 shl 31)) <> 0); 
      
      (*Remove comments for additional functionability
     {Is the Alt key pressed}
      if ((lParam and (1 shl 29)) <> 0) then begin
        IsAltPressed := TRUE;
      end else begin
        IsAltPressed := FALSE;
      end;

     {Is the Control key pressed}
      if ((GetKeyState(VK_CONTROL) and (1 shl 15)) <> 0) then begin
        IsCtrlPressed := TRUE;
      end else begin
        IsCtrlPressed := FALSE;
      end;

     {if the Shift key pressed}
      if ((GetKeyState(VK_SHIFT) and (1 shl 15)) <> 0) then begin
        IsShiftPressed := TRUE;
      end else begin
        IsShiftPressed := FALSE;
      end;
     *) 
      {if KeyUp then increment the key count} 
      if (KeyUp <> false) then 
        begin 
          inc (lpHookRec^.TheKeyCount)
        end { (KeyUp <> false) }; 
      
      Case wParam Of 
      {Was the enter key pressed?} 
      VK_RETURN: 
        begin 
          {if KeyUp} 
          if (KeyUp <> false) then 
            begin 
              {Post a bogus message to the window control in our app} 
              PostMessage (lpHookRec^.TheCtrlWinHandle, WM_KEYDOWN, 0, 0); 
              PostMessage (lpHookRec^.TheCtrlWinHandle, WM_KEYUP, 0, 0)
            end { (KeyUp <> false) }; 
          {if you wanted to swallow the keystroke then return -1} 
          {else if you want to allow the keystroke then return 0} 
          Result := 0; 
          exit
        end; {VK_RETURN} 
      {if the left arrow key is pressed then lets play a joke!} 
      VK_LEFT: 
        begin 
          {if KeyUp} 
          if (KeyUp <> false) then 
            begin 
              {Create a UpArrow keyboard event} 
              keybd_event (VK_RIGHT, 0, 0, 0); 
              keybd_event (VK_RIGHT, 0, KEYEVENTF_KEYUP, 0)
            end { (KeyUp <> false) }; 
          {Swallow the keystroke} 
          Result := -1; 
          exit
        end; {VK_LEFT} 
      end { case wParam }; {case wParam} 
      {Allow the keystroke} 
      Result := 0
    end; {HC_ACTION} 
  HC_NOREMOVE: 
    begin 
      {This is a keystroke message, but the keystroke message} 
      {has not been removed from the message queue, since an} 
      {application has called PeekMessage() specifying PM_NOREMOVE} 
      Result := 0; 
      exit
    end; 
  end { case code }; {case code} 
  if (code < 0) then 
    {Call the next hook in the hook chain} 
    Result := CallNextHookEx (lpHookRec^.TheHookHandle, code, wParam, lParam)
end; { KeyBoardProc } 


procedure StartKeyBoardHook 
  stdcall; 
begin { StartKeyBoardHook } 
  {if we have a process wide memory variable} 
  {and the hook has not already been set...} 
  if ((lpHookRec <> Nil) and (lpHookRec^.TheHookHandle = 0)) then 
    begin 
      {Set the hook and remember our hook handle} 
      lpHookRec^.TheHookHandle := SetWindowsHookEx (WH_KEYBOARD, @KeyBoardProc, 
        HInstance, 0)
    end { ((lpHookRec <> Nil) and (lpHookRec^.TheHookHandle = 0)) }
end; { StartKeyBoardHook } 


procedure StopKeyBoardHook 
  stdcall; 
begin { StopKeyBoardHook } 
  {if we have a process wide memory variable} 
  {and the hook has already been set...} 
  if ((lpHookRec <> Nil) and (lpHookRec^.TheHookHandle <> 0)) then 
    begin 
      {Remove our hook and clear our hook handle} 
      if (UnHookWindowsHookEx (lpHookRec^.TheHookHandle) <> false) then 
        begin 
          lpHookRec^.TheHookHandle := 0
        end { (UnHookWindowsHookEx (lpHookRec^.TheHookHandle) <> false) }
    end { ((lpHookRec <> Nil) and (lpHookRec^.TheHookHandle <> 0)) }
end; { StopKeyBoardHook } 


procedure DllEntryPoint (dwReason: DWord); 
begin { DllEntryPoint } 
  Case dwReason Of 
  Dll_Process_Attach: 
    begin 
      {if we are getting mapped into a process, then get} 
      {a pointer to our process wide memory mapped variable} 
      hObjHandle := 0; 
      lpHookRec := Nil; 
      MapFileMemory (sizeof (lpHookRec^))
    end; 
  Dll_Process_Detach: 
    begin 
      {if we are getting unmapped from a process then, remove} 
      {the pointer to our process wide memory mapped variable} 
      UnMapFileMemory
    end; 
  end { case dwReason }
end; { DllEntryPoint } 


Exports 
  KeyBoardProc name 'KEYBOARDPROC', 
  GetHookRecPointer name 'GETHOOKRECPOINTER', 
  StartKeyBoardHook name 'STARTKEYBOARDHOOK', 
  StopKeyBoardHook name 'STOPKEYBOARDHOOK'; 

begin 
  {Set our Dll's main entry point} 
  DLLProc := @DllEntryPoint; 
  {Call our Dll's main entry point} 
  DllEntryPoint (Dll_Process_Attach)
end.

Comments:

2006-06-10, 22:54:14
avvaikuna@yahoo.com from Singapore  
Could any one tell me where is this project full source code file?
2006-08-07, 07:09:29
anonymous from Iran  
I need to disable # Key In my KeyBoard.
Please Help me .
Note: I am using win Xp Sp1.
Zolfagharsoft@yahoo.com
2007-01-29, 04:49:29
kia  
rating
Hei nice code there, well for all those that doesn
t know what is this code i think i can help.
Start Delphi -> New -> Other... -> select DLL,
This code is a *.DLL(dynamic link library) this could be loaded by
any program made in C#, C++, VB(not sure coz it sux big time), etc.
compile this code then u obtain a project1.dll file, rename as u want
then make another program that loads dll and sends the command
to hook keyboard.
How can u use a dll in ur program? search on the net 'dll+use+delphi' u will
find some interesting articles there.
2007-02-28, 01:45:37   (updated: 2007-02-28, 01:46:15)
anonymous from India  
i need to keep track of all the key strokes available in browser...can u help me but in vc++ only
2007-04-12, 15:16:04
anonymous from Israel  
I wish I have it written in C or C++
2007-08-29, 21:44:25
bounthongv@gmail.com from Lao People's Democratic Republic  
rating
Hi, it is great, I find it very useful.

I have tried with this example, t is good for simulation of keyboard stoke. If anyone knows how we can do to send char (as we were typing it) which has higher virtual code let say 166 or something. I tried this, it does not work. And also, how the program would be, if we want to send unicode character which can have have very high number of code.
Any advises would be very appreciated.

Thong



2007-08-29, 23:14:10
bounthongv@gmail.com from Lao People's Democratic Republic  
rating
Hi, it is great, I find it very useful.

I have tried with this example, t is good for simulation of keyboard stoke. If anyone knows how we can do to send char (as we were typing it) which has higher virtual code let say 166 or something. I tried this, it does not work. And also, how the program would be, if we want to send unicode character which can have very high number of code.
Any advises would be very appreciated.

Thong



 

 

Email address (not necessary):

Rate as
Hide my email when showing my comment.
Please notify me once a day about new comments on this topic.
Please provide a valid email address if you select this option.
 
It seems that you are
from Los Angeles, US .

Info/ Feedback on this

Show city and country
Show country only
Hide my location
You can mark text as 'quoted' by putting [quote] .. [/quote] around it.
Please type in the code:
photo Add a picture:

Please do not post inappropriate pictures. Inappropriate pictures include pictures of minors and nudity. The owner of this web site reserves the right to delete such material.