【问题标题】:Problem with Keyboard hook proc键盘钩子过程的问题
【发布时间】:2010-04-04 22:14:05
【问题描述】:

背景:我的表单有一个 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;

【问题讨论】:

    标签: delphi winapi keyboard-hook twebbrowser


    【解决方案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 全局变量,如果它是唯一的形式,则仅挂钩/取消挂钩。

    【讨论】:

      【解决方案2】:

      背景:我的表单有一个 TWeb浏览器。我想关闭表格 使用 ESC 但 TWebBrowser 吃掉了 击键 - 所以我决定使用 键盘挂钩。

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

      【讨论】:

        【解决方案3】:

        嗯,这两个表单都已注册以接收键盘通知,因此它们都关闭了。 您需要在其中输入代码来决定“这个 ESC 适合我吗?”。也许通过确定您是否是焦点窗口。如果不是您的 ESCape,请不要关闭。

        但是,这一切似乎都相当激烈。必须有一种更简单、不显眼的方法来检测此 APP 中的 ESC,而无需监控整个系统的键盘。

        【讨论】:

        • 您好!这就是我使用 GetForegroundWindow (参见代码)的原因,但似乎有时两个窗口都会收到击键。 (但并非总是如此,这就是让我发疯的原因!)一旦我处理了击键,另一个窗口不应该得到它。为什么会这样?注意:我已经用 Google 搜索了几个小时,但没有找到比使用钩子更好的解决方案...
        • @Steve - 也许我误读了您的代码,但对我来说,您似乎只检查外观是否正在查看任何 TEmailDetailsForm,而不是此 TEmailDetailsForm。我也不明白为什么您在其中以及 CheckInstance 函数中有该代码。似乎多余,而且不正确。实际上,这可能是您的问题之一。 FormCreate 中的 CheckInstance 似乎会阻止 THIS 实例挂接键盘。为什么要这样做?每个实例不应该自己处理吗?
        • 你好克里斯。据我了解,KeyboardHookProc 必须在单元中,而不是在课堂内。 (否则它不起作用)因此,我必须使用全局变量(KeyboardHook:HHook),但是该类的每个实例都会覆盖 FormCreate 中的相同全局变量,从而创建两个键盘挂钩。我在这里错了吗?
        猜你喜欢
        • 2023-03-13
        • 2011-02-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-06
        • 1970-01-01
        • 2011-06-11
        • 2020-10-05
        • 2019-11-18
        相关资源
        最近更新 更多