-1

我已经设置了一个 WH_KEYBOARD_LL 挂钩来监视击键,代码在一段时间内可以正常工作,但突然停止,应用程序没有给出任何错误。

我开始调试它,并且在函数中有断点时,行为很快就出现了。看起来该函数被调用了大约 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.

基本上我将代码简化为这个,并且行为仍然存在。知道代码有什么问题吗?

4

1 回答 1

2

更新:此答案的第一部分提到了随后从问题中删除的代码。

您的呼叫GetMessage不正确且失败。您必须将指针传递给 MSG 结构。如果您不这样做,则不会从队列中拉出消息。

我不确定你是从哪里来GetMessageA的。在 Windows.pas 中声明的那个接受var参数的MSG参数。

您可能也应该发送消息。实际上,我不明白您为什么不使用标准消息泵:

while GetMessage(Msg, 0, 0, 0) do
begin
  TranslateMessage(Msg);
  DispatchMessage(Msg);
end;

在你说你在你的钩子函数中设置一个断点的问题中。文档有这样的说法:

挂钩过程应在比以下注册表项中的 LowLevelHooksTimeout 值中指定的数据条目更短的时间内处理消息:

HKEY_CURRENT_USER\控制面板\桌面

该值以毫秒为单位。如果钩子过程超时,系统将消息传递给下一个钩子。但是,在 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他发出了创建队列的调用,这改变了行为。


所有这一切,我怀疑你真正的应用程序的问题是不同的。我怀疑那里的问题是您传递nilGetMessage. 最终您的消息队列会填满。

于 2013-11-11T15:11:36.407 回答