HookProc只被调用几次

时间:2013-11-11 14:51:55

标签: delphi setwindowshookex

我已经设置了一个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.

基本上我将代码缩减为此,行为仍然存在。知道代码有什么问题吗?

1 个答案:

答案 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的第一个参数。最终你的消息队列填满了。