2010-08-23 180 views
4

我正在創建一個程序,它安裝鍵盤掛鉤來捕獲所有鍵並顯示與它們相關的一些文本。鍵盤掛鉤改變鍵的行爲

但是,我遇到了一個問題,那就是當安裝鉤子時,一些鍵會改變行爲。

我會看到發佈一個小的,但完整的測試程序,但現在我只會描述這個問題。

該問題在Windows 7 64位,.NET 4.0中是一個C#程序。我認爲這些都不重要。

我的掛鉤通過SetWindowsHookEx安裝,然後處理系統中處理的所有密鑰。

如果鉤子方法返回簡單或對鍵進行最小限度的處理(我會發布什麼會在一秒內改變行爲),鍵盤按照程序中的預期運行。但是,如果我從User32.dll中調用此函數ToAscii來確定我的鍵盤OemTilde或類似鍵上的哪個鍵,那麼任何「覆蓋下一個鍵」的鍵都將停止運行。我不知道這些鍵的正確名稱,但是兩個撇號類型,和', as well asand',停止運作。

例如,如果我打~然後N,它顯示如下:

  • 沒有鍵盤鉤子安裝:安裝ñ
  • 隨着鍵盤鉤子:N(注意沒有〜上文)

有誰知道爲什麼會發生這種情況,我該如何解決這個問題?

現在我會解決在其他程序中正確處理密鑰的問題,即使這意味着我無法在自己的程序中正確檢測到正確的密鑰序列。

一些更多信息:

如果我所說的ToAscii函數作爲鉤方法的一部分,則發生另一個問題。諸如¨之類的鍵被處理兩次,即,如果我點擊¨一次,記事本收到兩個¨¨字符,並且打N現在只是增加了N

但是,如果我使用BeginInvoke來處理單獨的線程上的鍵,從鍵盤掛接方法返回後,會出現第一個問題。


我的程序可能是有點在那個特殊:

  • 我不使用鍵盤的狀態(即。我傳過來的「Key State」256字節數組只是滿了0)
  • 我不在乎死鍵(在我的程序不會處理它們的意義上,我只關心它們,不希望我的程序,使它們沒用到系統的其餘部分)

這樣,我的代碼最終看上去如下:

private bool IsDeadKey(uint key) 
{ 
    return ((Hook.Interop.MapVirtualKey(key, 2) & 2147483648) == 2147483648); 
} 

void _Hook_KeyDown_Async(KeyDownEventArgs e) 
{ 
    var inBuffer = new byte[2]; 
    char key = '\0'; 
    if (!IsDeadKey((uint)e.KeyCode)) 
    { 
     int ascii = Hook.Interop.ToAscii((int) e.KeyCode, 
              e.ScanCode, 
              _KeyState, 
              inBuffer, 
              e.Flags); 
     if (ascii == 1) 
     { 
      key = Char.ToUpper((char) inBuffer[0]); 
     } 
    } 

    BeginInvoke(
     new Action<Keys, Boolean, Boolean, Boolean, Char>(ProcessKeyboardEvent), 
     e.KeyCode, e.Control, e.Shift, e.Alt, key); 
} 

回答

2

這些鍵被稱爲dead keys,你可能可以通過刪除調用ToAscii來解決這個問題。另請參閱以下相關主題:

ToAscii/ToUnicode in a keyboard hook destroys dead keys.

更新:我沒有看到你的代碼,但處理KeyboardProc回調函數的參數時,可以檢查你傳遞鍵盤消息何時code參數小於0?文檔說:

代碼 [IN] int

A碼掛鉤過程使用,以確定如何處理該消息。 如果代碼小於零,則掛鉤過程必須將消息傳遞給CallNextHookEx函數而不作進一步處理,並應返回CallNextHookEx返回的值。

有一個在MSDN爲setting up a managed hook樣本:

if (nCode < 0) 
{ 
    return CallNextHookEx(hHook, nCode, wParam, lParam); 
} 
else 
{ 
    // process message here 

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

刪除對'ToAscii'的調用會很好,但由於我的應用程序的全部重點是*顯示*您按哪些鍵,並且國際鍵盤上的許多鍵返回導致不同鍵的虛擬鍵碼,所以我需要*一些*獲取這些密鑰的方式。也許我可以緩存所有這些,儘管這可能是可能的... – 2010-08-23 15:43:32

+0

@Lasse V. Karlsen:我沒有深入研究,但也許你可以通過使用[MapVirtualKey](http:// msdn.microsoft.com/en-us/library/ms646306.aspx)函數? – 2010-08-23 15:47:37

+0

或者你可以在*調用'ToAscii' /'ToUnicode'之前檢查是否按下了死鍵? – 2010-08-23 15:55:56

1

什麼你可能看到的是該在涉及死鎖時嘗試映射密鑰的效果。鍵盤映射是一個相當複雜的過程,在產生這種行爲的某些類型的鍵周圍存在很多缺陷。

我鼓勵您閱讀Michael Kaplan撰寫的以下博客文章。它幫助我理清了一些錯誤。

+0

我其實並不太在乎死鎖。我的程序是一個攔截擊鍵的程序,根據配置文件和哪個應用程序處於活動狀態,可以顯示鍵的含義,以及按鍵順序的可視化表示以激活該功能。我想知道是否循環遍歷應用程序啓動時的所有組合鍵,並緩存ToAscii的結果會給我想要的。我做的*想要的是像OemTilde這樣的鍵來顯示正確的字母,但我實際上並不需要死鎖,我只需要避免打破它們的一般功能。 – 2010-08-23 15:45:25

2

的信息的關鍵位,從你的問題,你用這兩個鍵盤鉤子的缺失?簡單的WH_KEYBOARD_LL無法工作。您最終會使用程序的鍵盤狀態,而不是實際獲得按鍵的程序。死鑰匙確實有所作爲。

困難的WH_KEYBOARD需要一個鉤子,你不能在託管代碼中寫入。您需要一個可以在每個進程中注入的非託管DLL。一旦你明白了,我只是不打擾鍵盤掛鉤,不妨用WH_CALLWNDPROC記錄WM_CHAR消息。

一個示例DLL可用here

+0

我使用'_LL'版本,並且對於我來說我並不需要鍵盤狀態。我設法使用MapVirtualKey函數來查找某個鍵是否是死鍵,然後不對其調用ToAscii,因爲無論如何它對死鍵都沒有什麼用處。 – 2010-08-23 18:33:51