[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Keyboard grabber in other applications

Posted on 1998-03-07
3
Medium Priority
?
284 Views
Last Modified: 2010-05-18
How can I trap keys pressed in others applications and write to a file?
0
Comment
Question by:narabe
3 Comments
 
LVL 5

Expert Comment

by:ronit051397
ID: 1360247
I think it has something to do with Hooking.
0
 
LVL 4

Accepted Solution

by:
d003303 earned 300 total points
ID: 1360248
Yo,

like ronit said, you have to use hooks. Here is a sample code that traps all characters that are generated from the keyboard (systemwide).
The first listing is a DLL source (hook procedures have to resist in a DLL), the second is a sample application that displays all chars in a TMemo. You can easily change it to write to a file.
I have commented nearly everything, so you should understand what the whole thing is doing.

///////////////////////////////
// hook library

// to be hooktest.dpr
library hooktest;

uses
  Windows,
  Messages;

// because the DLL is mapped in each process space the hook event originates,
// it is instanciated for each such process. All global variables for all
// instances are independent. So we cannot init the DLL and set parameters on
// runtime to communicate with the host application. Therefore we use constants.
//
// MainAppClassName is used by the hook procedure to send the catched char to
// the host application. MainAppClassName is equivalent to the class name of
// the TWinControl descendant instance that carries the message handler.
//
// MainAppMessageNumber defines the message number that is sent to the host
// application. It should be wm_User + n. MainAppMessageID is just to make
// sure the message is sent from the hook procedure, because wm_User + n
// can be used by anyone. Can be any value you like.
//
// If you change any of these values, DO NOT FORGET to change them also in
// the host application !!!

const
  MainAppClassName     = 'TSpyKeyHookWnd';
  MainAppMessageNumber = wm_User + 1;
  MainAppMessageID     = 123;

// these variables have to be in the DLL
// because the keyboard state depends on
// the process space. The DLL is mapped
//
// originates.
// For performance reasons, this variables
// are global.

var
  KeyState : TKeyboardState;
  CharCode : LPARAM;

function KeyHookProc(nCode: integer; wp: WPARAM; lp: LPARAM): LRESULT; stdcall;
begin
  // we shall to this refering to the Win32API documentation
  if(nCode < 0) then
   begin
     Result := CallNextHookEx(0, nCode, wp, lp);
   end
  else
   // here we go
   begin
     // we only react on a global KEYDOWN event, check bit 32 in lParam
     // otherwise a message will be sent when a key is pressed and
     // a second time when the key is released
     if (lp and (1 shl 31)) = 0 then
      begin
        // clean old value
        CharCode := 0;
        // get keyboard state (shift, shift lock, etc.)
        GetKeyboardState(KeyState);
        // if this function returns 0, no character is translated
        // e.g. when ALT or SHIFT is pressed. We only accept non-dead keys.
        // if 2 is returned, we have a multibyte system. I will not
        // support it here.
        if ToASCII(wp, HiWord(lp), KeyState, @CharCode, 0) = 1
         // send a message containing the translated char in lParam.
         // see the comment on top of the project for a description
         // on this function
         then PostMessage(FindWindow(MainAppClassName, nil), MainAppMessageNumber, MainAppMessageID, CharCode);
      end;
     // we shall to this refering to the Win32API documentation
     Result := CallNextHookEx(0, nCode, wp, lp);
   end;
end;

exports
  KeyHookProc;

begin
end.

///////////////////////////////
// demo application

// to be hookexe.dpr
program hookexe;

uses
  Forms,
  _hookexe in '_hookexe.pas' {SpyKeyHookWnd};

{$R *.RES}

begin
  Application.Initialize;
  Application.CreateForm(TSpyKeyHookWnd, SpyKeyHookWnd);
  Application.Run;
end.

// to be _hookexe.pas
unit _hookexe;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ComCtrls, StdCtrls;

// KeyHookDllName is the name of the DLL containing the hook procedure.
// Equivalent to the DLLs project name.
//
// KeyHookProc is the name of the hook procedure in the DLL.
//
// MainAppMessageNumber and MainAppMessageID -> See DLL source code.

const
  KeyHookDllName       = 'hooktest.dll';
  HookProcName         = 'KeyHookProc';
  MainAppMessageNumber = wm_User + 1;
  MainAppMessageID     = 123;

type
  TSpyKeyHookWnd = class(TForm)
    StatusBar1: TStatusBar;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    LibHandle,
    HHookProc : THandle;
    HookProc  : function(nCode: integer; wp: WPARAM; lp: LPARAM): LRESULT; stdcall;
    // this is the handler for the MainAppMessageNumber message
    procedure WMUser_KeyPressedGlobal(var Message : TMessage); message MainAppMessageNumber;
  public
    { Public declarations }
  end;

var
  SpyKeyHookWnd: TSpyKeyHookWnd;

implementation

{$R *.DFM}

procedure TSpyKeyHookWnd.WMUser_KeyPressedGlobal(var Message : TMessage);
var KeyBuf : array[0..1] of Char;
begin
  // check if message is sent from DLL
  if Message.wParam = MainAppMessageID then
   begin
     // clean structure
     FillChar(KeyBuf[0], 2, 0);
     // translate character from lParam
     KeyBuf[0] := Chr(LoByte(LoWord(Message.lParam)));
     // put it into the memo
     Memo1.SetSelTextBuf(KeyBuf);
   end;
end;

procedure TSpyKeyHookWnd.FormCreate(Sender: TObject);
var DllName : string;
begin
  // get full qualified path to the DLL, assuming it is in the
  // same directory as the host application
  DllName := ExtractFilePath(Application.ExeName) + KeyHookDllName;
  // load DLL
  LibHandle := LoadLibrary(PChar(DllName));
  if LibHandle <> 0 then
   begin
     // find hook procedure address
     @HookProc := GetProcAddress(LibHandle, HookProcName);
     if @HookProc <> nil
      // insert the hook procedure into the systems hook chain
      then HHookProc := SetWindowsHookEx(WH_KEYBOARD, HookProc, LibHandle, 0)
      else Application.MessageBox('Failed to get HookProc address', 'Error', mb_ok);
   end
  else Application.MessageBox('Failed to load DLL', 'Error', mb_ok);
end;

procedure TSpyKeyHookWnd.FormDestroy(Sender: TObject);
begin
  if LibHandle <> 0 then
   begin
     if HHookProc <> 0
      // remove the hook procedure from the systems hook chain
      then UnhookWindowsHookEx(HHookProc);
     // unload DLL
     FreeLibrary(LibHandle);
   end;
end;

end.

// to be hookexe.dfm
object SpyKeyHookWnd: TSpyKeyHookWnd
  Left = 208
  Top = 113
  Width = 325
  Height = 195
  Caption = 'Systemwide Keystrokes - here they are'
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object Memo1: TMemo
    Left = 0
    Top = 0
    Width = 317
    Height = 168
    Align = alClient
    ReadOnly = True
    TabOrder = 0
  end
end

///////////////////////

Have fun,
Slash/d003303
0
 
LVL 7

Expert Comment

by:ahalya
ID: 1360249
Slash/d003303,

Beautiful !

0

Featured Post

[Webinar] Cloud and Mobile-First Strategy

Maybe you’ve fully adopted the cloud since the beginning. Or maybe you started with on-prem resources but are pursuing a “cloud and mobile first” strategy. Getting to that end state has its challenges. Discover how to build out a 100% cloud and mobile IT strategy in this webinar.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
Integration Management Part 2
We’ve all felt that sense of false security before—locking down external access to a database or component and feeling like we’ve done all we need to do to secure company data. But that feeling is fleeting. Attacks these days can happen in many w…
Suggested Courses

834 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question