Update: The first part of this answer referred to code that has subsequently been removed from the question.
Your call to GetMessage
is incorrect and fails. You have to pass a pointer to a MSG struct. If you don't messages will not be pulled from the queue.
I'm not sure where you are getting GetMessageA
from. The one declared in Windows.pas accepts a var
parameter for the MSG
parameter.
You probably ought to dispatch the messages too. In fact, I don't understand why you dont use the standard message pump:
while GetMessage(Msg, 0, 0, 0) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
In the question you say that you are setting a breakpoint in your hook function. The documentation has this to say:
The hook procedure should process a message in less time than the data entry specified in the LowLevelHooksTimeout value in the following registry key:
HKEY_CURRENT_USER\Control Panel\DesktopThe value is in milliseconds. If the hook procedure times out, the system passes the message to the next hook. However, on Windows 7 and later, the hook is silently removed without being called. There is no way for the application to know whether the hook is removed.
So, you'll need to stop using breakpoints. Instead use a technique like OutputDebugString
.
Even with these changes, the hook is never called when I run the program. Probably the reason why your hook is not being called is that your console application's main thread message queue is not active. The window that hosts the console is in a different process, conhost.exe. Your program is a console application that creates no windows, and so it won't have a message queue. When I run your program, the call to GetMessage
never returns, entirely as expected.
You can see that the hooking code works if you switch to a GUI subsystem application. For instance:
program LowLevelKeyboardHook;
uses
SysUtils, Windows, Forms;
var
hook : HHook;
function hookproc(code: Integer; wparam: WPARAM; lparam: LPARAM): LRESULT; stdcall;
begin
OutputDebugString('hookproc called');
result := CallNextHookEx(hook, code, wParam, lParam);
end;
var
Form: TForm;
begin
hook := SetWindowsHookEx(WH_KEYBOARD_LL, @hookproc, GetModuleHandle(0), 0);
Application.CreateForm(TForm, Form);
Application.Run;
end.
And take a read of Sertac's informative comments. His tests back up the hypothesis that your problem is down to the lack of a message queue. He throws in a call to PeekMessage
to create a queue, and that changes behaviour.
All this said, I suspect that your real application's problem is different. I suspect that the problem there is that you are passing nil
to the first parameter of GetMessage
. And eventually your message queue fills up.