2010-04-04 157 views
2

背景:我的表单有一个TWebBrowser。我想用ESC关闭窗体,但是TWebBrowser吃了击键 - 所以我决定用键盘挂钩。键盘钩子问题

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

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

我附上了一些示例代码。对导致此问题的原因有何看法?

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; 

回答

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全局变量,并且只有挂钩/解除挂钩(如果它是唯一的形式)。

+0

谢谢,这似乎工作得很好! – Steve 2010-04-06 18:43:56

0

背景:我的形式所具有的 TWebBrowser。我想用ESC关闭表格 ,但TWebBrowser吃了 击键 - 所以我决定去用 键盘挂钩。

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

+0

是的,这是行不通的。 – Steve 2010-04-05 10:30:30

0

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

但是,这一切似乎相当激烈。必须有一个更简单,非侵入式的方式来检测此应用程序内的ESC,而无需监视整个系统的键盘。

+0

你好!这就是为什么我使用GetForegroundWindow(见代码),但似乎有时两个窗口都会收到击键。 (但并非总是如此,这就是让我疯狂的原因!)一旦我处理了按键,其他窗口就不应该得到它。这是为什么发生?注意:我已经搜索了几个小时,但没有找到比使用钩子更好的解决方案... – Steve 2010-04-05 10:32:54

+0

@Steve - 也许我错误地阅读了你的代码,但对我来说,它看起来像你只看看看看是否你正在查看任何TEmailDetailsForm,而不是这个TEmailDetailsForm。我也不明白你为什么在那里有代码,并且也在CheckInstance函数中。似乎多余,而且不正确。其实,这可能是你的问题之一。 FormCreate中的CheckInstance好像会阻止THIS实例挂钩键盘。为什么这样做?每个实例不应该处理自己的处理? – 2010-04-05 11:01:16

+0

你好Chris。据我所知KeyboardHookProc必须在单位,而不是在课堂内。 (否则不起作用)因此,我必须使用全局变量(KeyboardHook:HHook),但这个类的每个实例都会覆盖FormCreate中相同的全局变量,从而创建两个键盘钩子。我在这里错了吗? – Steve 2010-04-05 12:33:23