Monday, March 2, 2020

Intercepting Keyboard Input With Delphi

Intercepting Keyboard Input With Delphi Consider for a moment creation of some fast arcade game. All the graphics are displayed, lets say, in a TPainBox. TPaintBox is unable to receive the input focus - no events are fired when the user presses a key; we cannot intercept cursor keys to move our battleship. Delphi help! Intercept Keyboard Input Most Delphi applications typically handle user input through specific event handlers, those that enable us to capture user keystrokes and process mouse movement. We know that focus is the ability to receive user input through the mouse or keyboard. Only the object that has the focus can receive a keyboard event. Some controls, such as TImage, TPaintBox, TPanel, and TLabel cannot receive focus. The primary purpose of most graphic controls is to display text or graphics. If we want to intercept keyboard input for controls that cannot receive the input focus well have to deal with Windows API, hooks, callbacks and messages. Windows Hooks Technically, a hook function is a callback function that can be inserted in the Windows message system so an application can access the message stream before other processing of the message takes place. Among many types of windows hooks, a keyboard hook is called whenever the application calls the GetMessage() or PeekMessage() function and there is a WM_KEYUP or WM_KEYDOWN keyboard message to process. To create a keyboard hook that intercepts all keyboard input directed to a given thread, we need to call SetWindowsHookEx API function. The routines that receive the keyboard events are application-defined callback functions called hook functions (KeyboardHookProc). Windows calls your hook function for each keystroke message (key up and key down) before the message is placed in the applications message queue. The hook function can process, change or discard keystrokes. Hooks can be local or global. The return value of SetWindowsHookEx is a handle to the hook just installed. Before terminating, an application must call the UnhookWindowsHookEx function to free system resources associated with the hook. Keyboard Hook Example As a demonstration of keyboard hooks, well create a project with graphical control that can receive key presses. TImage is derived from TGraphicControl, it can be used as a drawing surface for our hypothetical battle game. Since TImage is unable to receive keyboard presses through standard keyboard events well create a hook function that intercepts all keyboard input directed to our drawing surface. TImage Processing Keyboard Events Start new Delphi Project and place one Image component on a form. Set Image1.Align property to alClient. Thats it for the visual part, now we have to do some coding. First, well need some global variables: var   Ã‚  Form1: TForm1;   Ã‚  KBHook: HHook; {this intercepts keyboard input}   Ã‚  cx, cy : integer; {track battle ships position}   Ã‚  {callbacks declaration}   Ã‚  function KeyboardHookProc(Code: Integer; WordParam: Word; LongParam: LongInt): LongInt; stdcall; implementation ... To install a hook, we call SetWindowsHookEx in the OnCreate event of a form. procedure TForm1.FormCreate(Sender: TObject) ; begin   {Set the keyboard hook so we   can intercept keyboard input}   KBHook:SetWindowsHookEx(WH_KEYBOARD,   Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  {callback } KeyboardHookProc,   Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  HInstance,   Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  GetCurrentThreadId()) ;   {place the battle ship in   the middle of the screen}   cx : Image1.ClientWidth div 2;   cy : Image1.ClientHeight div 2;   Image1.Canvas.PenPos : Point(cx,cy) ; end; To free system resources associated with the hook, we must call the UnhookWindowsHookEx function in the OnDestroy event: procedure TForm1.FormDestroy(Sender: TObject) ; begin   Ã‚  {unhook the keyboard interception}   Ã‚  UnHookWindowsHookEx(KBHook) ; end; The most important part of this project is the KeyboardHookProc callback procedure used to process keystrokes. function KeyboardHookProc(Code: Integer; WordParam: Word; LongParam: LongInt) : LongInt; begin   case WordParam of   Ã‚  vk_Space: {erase battle ships path}   Ã‚  Ã‚  begin   Ã‚  Ã‚  Ã‚  with Form1.Image1.Canvas do   Ã‚  Ã‚  Ã‚  begin   Ã‚  Ã‚  Ã‚  Ã‚  Brush.Color : clWhite;   Ã‚  Ã‚  Ã‚  Ã‚  Brush.Style : bsSolid;   Ã‚  Ã‚  Ã‚  Ã‚  Fillrect(Form1.Image1.ClientRect) ;   Ã‚  Ã‚  Ã‚  end;   Ã‚  Ã‚  end;   Ã‚  vk_Right: cx : cx1;   Ã‚  vk_Left: cx : cx-1;   Ã‚  vk_Up: cy : cy-1;   Ã‚  vk_Down: cy : cy1;   end; {case}   If cx 2 then cx : Form1.Image1.ClientWidth-2;   If cx Form1.Image1.ClientWidth -2 then cx : 2;   If cy 2 then cy : Form1.Image1.ClientHeight -2 ;   If cy Form1.Image1.ClientHeight-2 then cy : 2;   with Form1.Image1.Canvas do   begin   Ã‚  Pen.Color : clRed;   Ã‚  Brush.Color : clYellow;   Ã‚  TextOut(0,0,Format(%d, %d,[cx,cy])) ;   Ã‚  Rectangle(cx-2, cy-2, cx2,cy2) ;   end;   Result:0; {To prevent Windows from passing the keystrokes   to the target window, the Result value must   be a nonzero value.} end; Thats it. We now have the ultimate keyboard processing code. Note just one thing: this code is in no way restricted to be used only with TImage. The KeyboardHookProc function serves as a general KeyPreview KeyProcess mechanism.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.