键盘钩子proc的问题

时间:2010-04-04 22:14:05

标签: delphi winapi keyboard-hook twebbrowser

背景:我的表单有一个TWebBrowser。我想用ESC关闭表单,但是TWebBrowser吃了键盘 - 所以我决定使用键盘钩。

问题是Form可以在多个实例中同时打开。

无论我做什么,在某些情况下,如果我的表单有两个实例打开,关闭其中一个也会关闭另一个。

我附上了一些示例代码。有关导致问题的原因的任何想法?

var
  EmailDetailsForm: TEmailDetailsForm;
  KeyboardHook: HHook;

implementation

function KeyboardHookProc(Code: Integer; wParam, lParam: LongInt): LongInt; stdcall;
var
  hWnd: THandle;
  I: Integer;
  F: TForm;
begin
  if Code < 0 then
    Result := CallNextHookEx(KeyboardHook, Code, wParam, lParam)
  else begin
    case wParam of
      VK_ESCAPE:  
        if (lParam and $80000000) <> $00000000 then
        begin
          hWnd := GetForegroundWindow;
          for I := 0 to Screen.FormCount - 1 do
          begin
            F := Screen.Forms[I];
            if F.Handle = hWnd then
              if F is TEmailDetailsForm then
              begin
                PostMessage(hWnd, WM_CLOSE, 0, 0);
                Result := HC_SKIP;
                break;
              end;
          end; //for
        end; //if
      else
        Result := CallNextHookEx(KeyboardHook, Code, wParam, lParam);
    end;  //case
  end;  //if
end;

function TEmailDetailsForm.CheckInstance: Boolean;
var
  I, J: Integer;
  F: TForm;
begin
  Result := false;

  J := 0;

  for I := 0 to Screen.FormCount - 1 do
  begin
    F := Screen.Forms[I];
    if F is TEmailDetailsForm then
    begin
      J := J + 1;
      if J = 2 then
      begin
        Result := true;
        break;
      end;
    end;
  end;
end;

procedure TEmailDetailsForm.FormCreate(Sender: TObject);
begin
    if not CheckInstance then    
      KeyboardHook := SetWindowsHookEx(WH_KEYBOARD, @KeyboardHookProc, 0, GetCurrentThreadId());
end;

procedure TEmailDetailsForm.FormDestroy(Sender: TObject);
begin
    if not CheckInstance then
      UnHookWindowsHookEx(KeyboardHook);
end;

3 个答案:

答案 0 :(得分:1)

您可以使用TApplicationEvents.OnMessage执行此操作。使用以下代码在应用程序的主窗体上删除TApplicationEvents组件:

procedure TMainForm.ApplicationEvents1Message(var Msg: tagMSG;
  var Handled: Boolean);
var
  C: TControl;
  H: HWND;
begin
  if (Msg.message = WM_KEYDOWN) and (Msg.wParam = VK_ESCAPE) then begin
    H := Msg.hwnd;
    while GetParent(H) <> 0 do
      H := GetParent(H);
    C := FindControl(H);
    if C is TEmailDetailsForm then begin
      TEmailDetailsForm(C).Close;
      Handled := True;
    end;
  end;
end;

如果你想继续使用键盘钩子,你应该只挂钩一次,而不是每个表格一次,特别是因为你覆盖了一个全局变量。尝试添加一个HookCount全局变量,如果它是唯一的形式,则只挂钩/取消挂钩。

答案 1 :(得分:0)

  

背景:我的表格有一个   TWebBrowser。我想关闭表格   用ESC但是TWebBrowser吃掉了   击键 - 所以我决定去一个   键盘钩。

可能有一个更简单的解决方案。您是否尝试将表单的KeyPreview属性设置为True

答案 2 :(得分:0)

好吧,两个表格都已注册接收键盘通知,因此它们都关闭了。 你需要在那里放置代码来决定“这是我的ESC吗?”。也许通过确定你是否是焦点窗口。如果它不是您的ESCape,那么请不要关闭。

但是,这一切看起来都相当激烈。必须有一种更简单,非突兀的方法来检测此APP中的ESC,而无需监控整个系统的键盘。