2010-04-04 77 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