2012-03-10 44 views
5

我有一個程序有幾個自定義控件。其中一個自定義控件是文本輸入控件。由於窗口在點擊時並不自動接收鍵盤焦點,因此我在程序中創建了一個鼠標鉤子,用戶在該窗口中單擊時在窗口上調用SetFocus()。但是,有一個問題。SetFocus()失敗並顯示有效的窗口句柄

如果另一個程序在您點擊我的程序窗口(或該窗口中的任何控件)時有焦點,則SetFocus()失敗。然後我必須再次點擊才能成功。下面的代碼:

LRESULT CALLBACK kbfProc(int nCode, WPARAM wParam, LPARAM lParam) // Keyboard focus switching procedure 
{ 
    switch(nCode) 
    { 
     case HC_ACTION: 
     { 
      if(wParam == WM_LBUTTONDOWN || wParam == WM_NCLBUTTONDOWN) 
      { 
       MOUSEHOOKSTRUCT * mhs = (MOUSEHOOKSTRUCT*) lParam; 

       if(SetFocus(mhs->hwnd) == NULL) 
       { 
        printf("SetFocus(Hwnd = %.8x) failed. Error code: %lu\n", mhs->hwnd, GetLastError()); 
       } else { 

        printf("SetFocus(Hwnd = %.8x) returned success.\n", mhs->hwnd); 
       } 
      } 

     } 
     break; 
    } 

    return CallNextHookEx(0, nCode, wParam, lParam); 
} 

而那些printf的調用的輸出是這樣的:

SetFocus(Hwnd = 00410c06) failed. Error code: 87 
SetFocus(Hwnd = 00410c06) returned success. 
SetFocus(Hwnd = 01740fc8) failed. Error code: 87 
SetFocus(Hwnd = 01740fc8) returned success. 

錯誤代碼87是ERROR_INVALID_PARAMETER,但我顯然通過一個有效的窗口句柄功能,那麼爲什麼它失敗了嗎?

+0

您的kbfProc回調方法是假定每當鼠標移動時調用?向我們展示對WINAPI方法的調用。 此外,我認爲最好通過表單加載事件來設置焦點。 – 2012-03-10 04:00:00

+0

@opatachibueze WINAPI方法?表單加載事件?這不是來自.NET嗎?我正在使用C++ ... – Gogeta70 2012-03-10 04:04:26

+0

你的代碼使用什麼部分LRESULT CALLBACK kbfproc – 2012-03-10 04:10:16

回答

1

我找到了解決方案。我最終做了大量的谷歌搜索,並嘗試了很多不同的東西,最終我遇到了this網頁。這是一個冗長的閱讀,它有點羅嗦,很難理解,但它有很多信息。我最終在我的主窗口的WM_ACTIVATE處理程序中添加了一些代碼,用於搜索窗口被激活時單擊的子窗口。這裏的所有代碼:

這裏的鉤子程序:

LRESULT CALLBACK kbfProc(int nCode, WPARAM wParam, LPARAM lParam) 
{ 
    switch(nCode) 
    { 
     case HC_ACTION: 
     { 
      if(wParam == WM_LBUTTONDOWN || wParam == WM_NCLBUTTONDOWN) 
      { 
       MOUSEHOOKSTRUCT * mhs = (MOUSEHOOKSTRUCT*) lParam; 

       BringWindowToTop(MainWindow->t_hwnd); 
       SetFocus(mhs->hwnd); 
      } 

     } 
     break; 
    } 

    return CallNextHookEx(0, nCode, wParam, lParam); 
} 

這是我放在WM_ACTIVATE處理程序的代碼:

case WM_ACTIVATE: 
    { 
     unsigned long state = (unsigned long) wParam & 0x0000FFFF; 
     unsigned long mState = (unsigned long) wParam & 0xFFFF0000; 

     if(state != 0) 
     { 
      ...[some code here]... 

      FocusChildWindow(hwnd); 
     } 

     ...[some code here]... 
    } 
    break; 

而這裏的FocusChildWindow()函數:

void FocusChildWindow(HWND hwnd) 
{ 
    POINT mpos; 
    GetCursorPos(&mpos); 

    RECT wr; 
    GetWindowRect(hwnd, &wr); 

    mpos.x -= wr.left; 
    mpos.y -= wr.top; 

    HWND cw = ChildWindowFromPoint(hwnd, mpos); 

    if(cw == NULL || cw == hwnd) 
    { 
     SetFocus(hwnd); 
    } else { 

     GetCursorPos(&mpos); 
     HWND cw2; 

     while(1) 
     { 
      POINT sc = mpos; 
      MapWindowPoints(HWND_DESKTOP, cw, &sc, 1); 

      cw2 = ChildWindowFromPoint(cw, sc); 

      if(cw2 == NULL || cw2 == cw) 
      { 
       SetFocus(cw); 
       break; 
      } else { 

       cw = cw2; 
      } 


     } 

    } 

} 

它就像一個魅力。再次感謝大家對於這個問題的意見和想法。

3

無論何時調用SetFocus,必須將窗口連接到調用線程的消息隊列,否則SetFocus將返回無效。要解決此問題,請在撥打SetFocus之前先使用SetForegroundWindow,鼠標移過窗口。

+0

我試過你的建議,但我仍然得到完全相同的結果。 – Gogeta70 2012-03-10 04:31:04

+0

嗯,在兩種情況下,SetFocus的成功返回值是多少?它應該返回以前關注的窗口。另外,你有沒有嘗試過'SetActiveWindow'? – 2012-03-10 04:52:39

+0

您是否嘗試過'AttachThreadInput(GetWindowThreadProcessId(window),GetCurrentThreadId(),true)'? – 2012-03-11 01:01:55