我已经设置了一个WH_KEYBOARD_LL钩子来监视击键,代码工作正常一段时间但突然停止了应用程序不会出现任何单一错误。
我开始调试它,并且在函数中有一个断点时,行为出现得更早。看起来这个函数被称为aproximatelly 6次,直到它停止接收键盘事件。
function hookproc(code: Integer; wparam: WPARAM;lparam: LPARAM): LRESULT; stdcall;
begin
result := CallNextHookEx(hook, code, wParam, lParam); // I have put breakpoint here
end;
procedure Start();
begin
hook := SetWindowsHookEx(WH_KEYBOARD_LL,@hookproc,GetModuleHandle(nil),0);
end;
procedure TMyApplication.DoRun;
var
msg : tagMSG;
begin
Start();
ZeroMemory(@msg,sizeof(msg));
while GetMessage(Msg, 0, 0, 0) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end.
基本上我将代码缩减为此,行为仍然存在。知道代码有什么问题吗?
答案 0 :(得分:2)
更新:此答案的第一部分提到了随后已从问题中移除的代码。
您对GetMessage
的来电不正确且失败。您必须将指针传递给MSG结构。如果没有,则不会从队列中提取消息。
我不确定你从哪里得到GetMessageA
。在Windows.pas中声明的那个接受var
参数的MSG
参数。
您可能也应该发送消息。事实上,我不明白为什么你不使用标准的消息泵:
while GetMessage(Msg, 0, 0, 0) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
在问题中,您说您正在挂钩函数中设置断点。 documentation可以这样说:
钩子程序应该在比数据更短的时间内处理消息 在以下LowLevelHooksTimeout值中指定的条目 注册表项:
HKEY_CURRENT_USER\Control Panel\Desktop该值以毫秒为单位。如果挂钩程序超时,则 系统将消息传递给下一个钩子。但是,在Windows 7和 之后,无需调用就可以无提示地删除钩子。没有 应用程序知道钩子是否被移除的方式。
因此,您需要停止使用断点。而是使用OutputDebugString
等技术。
即使进行了这些更改,在运行程序时也永远不会调用钩子。可能没有调用钩子的原因是控制台应用程序的主线程消息队列不活动。托管控制台的窗口位于另一个进程conhost.exe中。您的程序是一个不创建窗口的控制台应用程序,因此它没有消息队列。当我运行你的程序时,对GetMessage
的调用永远不会返回,完全如预期的那样。
如果切换到GUI子系统应用程序,您可以看到挂钩代码有效。 例如:
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.
阅读Sertac的内容丰富的评论。他的测试支持了一个假设,即您的问题归结为缺少消息队列。他调用PeekMessage
来创建一个队列,这会改变行为。
所有这些都说,我怀疑你的真实应用程序的问题是不同的。我怀疑问题是你将nil
传递给GetMessage
的第一个参数。最终你的消息队列填满了。