2014-01-06 26 views
1

我寫的實用程序需要使用SetWindowsHookEx函數來捕獲LowLevelKeyboardProc回調的按鍵。鍵捕獲:WM_KEYDOWN最後一個鍵狀態位總是== 0

我已經實施在this MSDN blog上找到的代碼。

正確的鍵碼被捕獲,但我似乎無法使用從答案帶到這個StackOverflow的問題掩碼時,爲了獲得第30位的正確值:Get 30th bit of the lParam param in WM_KEYDOWN message

private const int LAST_KEY_STATE_BITMASK = 0x40000000; 

此位應根據Microsoft documentation,允許我檢測鑰匙是否被扣下。

我只想在第一次按下按鍵時未按住keydown事件; keyup是不夠的。

我使用的嘗試,並獲得第30位的值的代碼是:

int vkCode = Marshal.ReadInt32(lParam); 
    int lastKeyState = (vkCode & LAST_KEY_STATE_BITMASK); 

其中,從我的理解,應該返回0時鍵沒有被按住1時是。

我也嘗試使用lParam.ToInt32()(int)lParam代替vkCode,但無濟於事。

請幫忙!

下面是完整的程序,因爲它是在其目前的狀態:

using System; 
using System.Diagnostics; 
using System.Windows.Forms; 
using System.Runtime.InteropServices; 

public class KeyCapture 
{ 
    private const int WH_KEYBOARD_LL = 13; 
    private const int WM_KEYDOWN = 0x0100; 
    private const int LAST_KEY_STATE_BITMASK = 0x40000000; 

    private static LowLevelKeyboardProc _proc = HookCallback; 
    private static IntPtr _hookID = IntPtr.Zero; 

    public static void Main() 
    { 
     _hookID = SetHook(_proc); 
     Application.Run(); 
     UnhookWindowsHookEx(_hookID); 
    } 

    private static IntPtr SetHook(LowLevelKeyboardProc proc) 
    { 
     using (Process curProcess = Process.GetCurrentProcess()) 
     using (ProcessModule curModule = curProcess.MainModule) 
     { 
      return SetWindowsHookEx(WH_KEYBOARD_LL, proc, 
       GetModuleHandle(curModule.ModuleName), 0); 
     } 
    } 

    private delegate IntPtr LowLevelKeyboardProc(
     int nCode, IntPtr wParam, IntPtr lParam); 

    private static IntPtr HookCallback(
     int nCode, IntPtr wParam, IntPtr lParam) 
    { 
     if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) 
     { 
      int vkCode = Marshal.ReadInt32(lParam); 
      int lastKeyState = (vkCode & LAST_KEY_STATE_BITMASK); 

      Console.WriteLine((Keys)vkCode + "|" + vkCode + "|" + lastKeyState); 
     } 
     return CallNextHookEx(_hookID, nCode, wParam, lParam); 
    } 

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern IntPtr SetWindowsHookEx(int idHook, 
     LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); 

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    private static extern bool UnhookWindowsHookEx(IntPtr hhk); 

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, 
     IntPtr wParam, IntPtr lParam); 

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern IntPtr GetModuleHandle(string lpModuleName); 
} 
+1

[閱讀文檔](http://msdn.microsoft.com/en-us/library/windows/desktop/ms644985.aspx)。您似乎認爲傳遞給'LowLevelKeyboardProc'的lParam'遵循與傳遞給'WM_KEYDOWN'消息的'lParam'相同的約定。情況並非如此:在'LowLevelKeyboardProc'中,'lParam'是「一個指向'KBDLLHOOKSTRUCT'結構的指針。」 –

+0

@IgorTandetnik現在我覺得很傻!在用有意義的變量名稱注視C#一天之後,我必須撇清這一點。非常感謝您的快速回復。 – cpoliver

+0

@IgorTandetnik請原諒我的無知(自從您的回覆以來,我一直在無情地尋找),但是您能否瞭解我如何閱讀WM_KEYUP消息的lParam以獲取我感興趣的位? – cpoliver

回答

0

由於伊戈爾好心指出的那樣,我不得不這樣管理自己。

該解決方案只是一個靜態列表,其中列出了任何按鍵的靜態列表,這些列表在沒有按鍵事件的情況下收到。

private static readonly List<int> KeysDown = new List<int>(); 

private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) 
{ 
    if (nCode >= 0) 
    { 
     var keycode = Marshal.ReadInt32(lParam); 

     if (wParam == (IntPtr)WM_KEYUP) 
     { 
      KeysDown.RemoveAll(k => k == keycode); 
     } 

     if (wParam == (IntPtr)WM_KEYDOWN) 
     { 
      if (!KeysDown.Contains(keycode)) 
      { 
       // Do stuff! 
      } 

      KeysDown.Add(keycode); 
     } 

    } 

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