Manny
Manny

Reputation: 566

Hooking the mouse and keyboard in Lazarus

I am trying to hook mouse and keyboard events and log them to the Memo1 (for mouse) and Memo2 (for keyboard). The code i am trying to compile to:

unit Unit1;

{$mode objfpc}{$H+}


interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, windows;

type

  { TForm1 }

  TForm1 = class(TForm)
    Memo1: TMemo;
    Memo2: TMemo;
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    { private declarations }
  public
    function LowLevelKeybdHookProc(nCode: LongInt; WPARAM: WPARAM; lParam : LPARAM) : LRESULT; stdcall;
    function LowLevelMouseHookProc(nCode: LongInt; WPARAM: WPARAM; lParam : LPARAM) : LRESULT; stdcall;
    { public declarations }
  end;
  KeybdLLHookStruct = record
    vkCode      : cardinal;
    scanCode    : cardinal;
    flags       : cardinal;
    time        : cardinal;
    dwExtraInfo : cardinal;
  end;
  MouseLLHookStruct = record
    pt          : TPoint;
    mouseData   : cardinal;
    flags       : cardinal;
    time        : cardinal;
    dwExtraInfo : cardinal;
  end;

var
  Form1: TForm1;
  mHook : cardinal;
  kHook : cardinal;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
UnhookWindowsHookEx(kHook);
UnhookWindowsHookEx(mHook);
end;

procedure TForm1.FormCreate(Sender: TObject);
const
  wh_keybd_ll = 13;
  wh_mouse_ll = 14;
begin
kHook := SetWindowsHookEx(wh_keybd_ll, @LowLevelKeybdHookProc, hInstance, 0);
mHook := SetWindowsHookEx(wh_mouse_ll, @LowLevelMouseHookProc, hInstance, 0);
end;


function TForm1.LowLevelKeybdHookProc(nCode: LongInt; WPARAM: WPARAM; lParam : LPARAM) : LRESULT; stdcall;
// possible wParam values: WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, WM_SYSKEYUP
var
  info : ^KeybdLLHookStruct absolute lParam;
  lpChar : word;
  kState : TKeyboardState;
begin
result := CallNextHookEx(kHook, nCode, wParam, lParam);
with info^ do
case wParam of
  wm_keydown : begin
    GetKeyboardState(kState);
    if ToAscii(vkCode, scanCode, kState, @lpChar, 0) > 0 then Form1.Memo2.Lines.Append(format('pressed key -- %s', [char(lpChar)]));
  end;
end;
end;

function TForm1.LowLevelMouseHookProc(nCode: LongInt; WPARAM: WPARAM; lParam : LPARAM) : LRESULT; stdcall;
// possible wParam values: WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_RBUTTONDOWN, WM_RBUTTONUP
var
  info : ^MouseLLHookStruct absolute lParam;
begin
result := CallNextHookEx(mHook, nCode, wParam, lParam);
with info^ do
case wParam of
  wm_lbuttondown : Form1.Memo1.Lines.Append(format('pressed left button (%d, %d)'    , [pt.x, pt.y]));
  wm_lbuttonup   : Form1.Memo1.Lines.Append(format('released left button (%d, %d)'   , [pt.x, pt.y]));
  wm_mbuttondown : Form1.Memo1.Lines.Append(format('pressed middle button (%d, %d)'  , [pt.x, pt.y]));
  wm_mbuttonup   : Form1.Memo1.Lines.Append(format('released middle button (%d, %d)' , [pt.x, pt.y]));
  wm_rbuttondown : Form1.Memo1.Lines.Append(format('pressed right button (%d, %d)'   , [pt.x, pt.y]));
  wm_rbuttonup   : Form1.Memo1.Lines.Append(format('released right button (%d, %d)'  , [pt.x, pt.y]));
  wm_mousewheel  : begin
    if smallInt(mouseData shr 16) > 0
    then Form1.Memo1.Lines.Append('scrolled wheel (up)')
    else Form1.Memo1.Lines.Append('scrolled wheel (down)');
  end;
end;
end;

end.

But it returns these errors:

unit1.pas(64,62) Error: Incompatible type for arg no. 2: Got "<procedure variable type of function(LongInt,LongInt,LongInt):LongInt of object;StdCall>", expected "<procedure variable type of function(LongInt,LongInt,LongInt):LongInt;StdCall>"
unit1.pas(65,62) Error: Incompatible type for arg no. 2: Got "<procedure variable type of function(LongInt,LongInt,LongInt):LongInt of object;StdCall>", expected "<procedure variable type of function(LongInt,LongInt,LongInt):LongInt;StdCall>"

I can't understand what is "LongInt of object" and how to fix this.

Upvotes: 0

Views: 3617

Answers (1)

David Heffernan
David Heffernan

Reputation: 613442

The of object indicates that you have supplied a method pointer. That is an instance or a class method. In your case you are passing an instance method where a plain procedure is required.

Solve the problem by using procedures rather than methods. You'll need to get hold of the form by a means other than the Self pointer, but then the code in the question wasn't using Self anyway. As an aside, that in itself is a bad move. Always use Self rather than a global variable that happens to have the same value as Self.

Upvotes: 3

Related Questions