I am trying to duplicate the code in Intercepting Keyboard Input With Delphi. Following is code:
...
type
TForm1 = class(TForm)
ListBox1: TListBox;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
KBHook: HHook; {this intercepts keyboard input}
{callback's declaration}
function KeyboardHookProc(Code: Integer; WordParam: Word;
LongParam: LongInt): LongInt; stdcall;
public
{ Public declarations }
end;
...
...
procedure TForm1.FormCreate(Sender: TObject);
begin
{Set the keyboard hook so we can intercept keyboard input}
KBHook := SetWindowsHookEx( WH_KEYBOARD,
{callback >} @KeyboardHookProc,
HInstance,
GetCurrentThreadId() );
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
{unhook the keyboard interception}
UnHookWindowsHookEx(KBHook) ;
end;
function TForm1.KeyboardHookProc(Code: Integer; WordParam: Word;
LongParam: LongInt): LongInt;
begin
ListBox1.Items.Add( 'Code: ' + Code.ToString);
ListBox1.Items.Add( ' -- WordParam: ' + WordParam.ToString);
ListBox1.Items.Add( ' -- LongParam: ' + LongParam.ToString);
ListBox1.Items.Add( '' );
Result := 0;
{To prevent Windows from passing the keystrokes
to the target window, the Result value must be a nonzero value.}
end;
...
The code does not compile. The error:
[dcc32 Error] Unit1.pas(43): E2036 Variable required
The error points to the @KeyboardHookProc
, the second argument to SetWindowsHookEx
function.
I then tried the WH_SHELL
and WH_GETMESSAGE
with their own callback procedures, also without success, with the same error.
What did I miss?
You cannot use a non-static class method as a hook callback (well, not without thunking it, anyway). It has a hidden Self
parameter that the API has no knowledge of.
To remove that Self
parameter, you need to declare the method as class ... static
, eg:
type
TForm1 = class(TForm)
ListBox1: TListBox;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
KBHook: HHook; {this intercepts keyboard input}
{callback's declaration}
class function KeyboardHookProc(Code: Integer; WordParam: WPARAM;
LongParam: LPARAM): LRESULT; stdcall; static;
public
{ Public declarations }
end;
var
Form1: TForm1;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
{Set the keyboard hook so we can intercept keyboard input}
KBHook := SetWindowsHookEx( WH_KEYBOARD,
{callback >} @KeyboardHookProc,
HInstance,
GetCurrentThreadId() );
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
{unhook the keyboard interception}
UnHookWindowsHookEx(KBHook) ;
end;
class function TForm1.KeyboardHookProc(Code: Integer; WordParam: WPARAM;
LongParam: LPARAM): LRESULT;
begin
Form1.ListBox1.Items.Add( 'Code: ' + Code.ToString);
Form1.ListBox1.Items.Add( ' -- WordParam: ' + WordParam.ToString);
Form1.ListBox1.Items.Add( ' -- LongParam: ' + LongParam.ToString);
Form1.ListBox1.Items.Add( '' );
Result := 0;
{To prevent Windows from passing the keystrokes
to the target window, the Result value must be a nonzero value.}
end;
Alternatively, you can use a free-standing function (as the article you linked to is doing) instead of using a class method, eg:
type
TForm1 = class(TForm)
ListBox1: TListBox;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
KBHook: HHook; {this intercepts keyboard input}
public
{ Public declarations }
end;
var
Form1: TForm1;
...
{callback's declaration}
function KeyboardHookProc(Code: Integer; WordParam: WPARAM;
LongParam: LPARAM): LRESULT; stdcall;
begin
Form1.ListBox1.Items.Add( 'Code: ' + Code.ToString);
Form1.ListBox1.Items.Add( ' -- WordParam: ' + WordParam.ToString);
Form1.ListBox1.Items.Add( ' -- LongParam: ' + LongParam.ToString);
Form1.ListBox1.Items.Add( '' );
Result := 0;
{To prevent Windows from passing the keystrokes
to the target window, the Result value must be a nonzero value.}
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
{Set the keyboard hook so we can intercept keyboard input}
KBHook := SetWindowsHookEx( WH_KEYBOARD,
{callback >} @KeyboardHookProc,
HInstance,
GetCurrentThreadId() );
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
{unhook the keyboard interception}
UnHookWindowsHookEx(KBHook) ;
end;