2017-07-08 32 views
1

這是我用來鉤住鍵盤按鍵的班級。有沒有什麼辦法讓全局鉤住鼠標動作就好像我在鉤住鍵盤按鍵?

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Runtime.InteropServices; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows.Forms; 

public class Hook : IDisposable 
{ 
    bool Global = false; 

    public delegate void LocalKeyEventHandler(Keys key, bool Shift, bool Ctrl, bool Alt); 
    public event LocalKeyEventHandler KeyDown; 
    public event LocalKeyEventHandler KeyUp; 

    public delegate int CallbackDelegate(int Code, int W, int L); 

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
    public struct KBDLLHookStruct 
    { 
     public Int32 vkCode; 
     public Int32 scanCode; 
     public Int32 flags; 
     public Int32 time; 
     public Int32 dwExtraInfo; 
    } 

    [DllImport("user32", CallingConvention = CallingConvention.StdCall)] 
    private static extern int SetWindowsHookEx(HookType idHook, CallbackDelegate lpfn, int hInstance, int threadId); 

    [DllImport("user32", CallingConvention = CallingConvention.StdCall)] 
    private static extern bool UnhookWindowsHookEx(int idHook); 

    [DllImport("user32", CallingConvention = CallingConvention.StdCall)] 
    private static extern int CallNextHookEx(int idHook, int nCode, int wParam, int lParam); 

    [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)] 
    private static extern int GetCurrentThreadId(); 

    public enum HookType : int 
    { 
     WH_JOURNALRECORD = 0, 
     WH_JOURNALPLAYBACK = 1, 
     WH_KEYBOARD = 2, 
     WH_GETMESSAGE = 3, 
     WH_CALLWNDPROC = 4, 
     WH_CBT = 5, 
     WH_SYSMSGFILTER = 6, 
     WH_MOUSE = 7, 
     WH_HARDWARE = 8, 
     WH_DEBUG = 9, 
     WH_SHELL = 10, 
     WH_FOREGROUNDIDLE = 11, 
     WH_CALLWNDPROCRET = 12, 
     WH_KEYBOARD_LL = 13, 
     WH_MOUSE_LL = 14 
    } 

    private int HookID = 0; 
    CallbackDelegate TheHookCB = null; 

    //Start hook 
    public Hook(bool Global) 
    { 
     this.Global = Global; 
     TheHookCB = new CallbackDelegate(KeybHookProc); 
     if (Global) 
     { 
      HookID = SetWindowsHookEx(HookType.WH_KEYBOARD_LL, TheHookCB, 
       0, //0 for local hook. eller hwnd til user32 for global 
       0); //0 for global hook. eller thread for hooken 
     } 
     else 
     { 
      HookID = SetWindowsHookEx(HookType.WH_KEYBOARD, TheHookCB, 
       0, //0 for local hook. or hwnd to user32 for global 
       GetCurrentThreadId()); //0 for global hook. or thread for the hook 
     } 
    } 

    bool IsFinalized = false; 
    ~Hook() 
    { 
     if (!IsFinalized) 
     { 
      UnhookWindowsHookEx(HookID); 
      IsFinalized = true; 
     } 
    } 
    public void Dispose() 
    { 
     if (!IsFinalized) 
     { 
      UnhookWindowsHookEx(HookID); 
      IsFinalized = true; 
     } 
    } 

    //The listener that will trigger events 
    private int KeybHookProc(int Code, int W, int L) 
    { 
     KBDLLHookStruct LS = new KBDLLHookStruct(); 
     if (Code < 0) 
     { 
      return CallNextHookEx(HookID, Code, W, L); 
     } 
     try 
     { 
      if (!Global) 
      { 
       if (Code == 3) 
       { 
        IntPtr ptr = IntPtr.Zero; 

        int keydownup = L >> 30; 
        if (keydownup == 0) 
        { 
         if (KeyDown != null) KeyDown((Keys)W, GetShiftPressed(), GetCtrlPressed(), GetAltPressed()); 
        } 
        if (keydownup == -1) 
        { 
         if (KeyUp != null) KeyUp((Keys)W, GetShiftPressed(), GetCtrlPressed(), GetAltPressed()); 
        } 
        //System.Diagnostics.Debug.WriteLine("Down: " + (Keys)W); 
       } 
      } 
      else 
      { 
       KeyEvents kEvent = (KeyEvents)W; 

       Int32 vkCode = Marshal.ReadInt32((IntPtr)L); //Leser vkCode som er de første 32 bits hvor L peker. 

       if (kEvent != KeyEvents.KeyDown && kEvent != KeyEvents.KeyUp && kEvent != KeyEvents.SKeyDown && kEvent != KeyEvents.SKeyUp) 
       { 
       } 
       if (kEvent == KeyEvents.KeyDown || kEvent == KeyEvents.SKeyDown) 
       { 
        if (KeyDown != null) KeyDown((Keys)vkCode, GetShiftPressed(), GetCtrlPressed(), GetAltPressed()); 
       } 
       if (kEvent == KeyEvents.KeyUp || kEvent == KeyEvents.SKeyUp) 
       { 
        if (KeyUp != null) KeyUp((Keys)vkCode, GetShiftPressed(), GetCtrlPressed(), GetAltPressed()); 
       } 
      } 
     } 
     catch (Exception) 
     { 
      //Ignore all errors... 
     } 

     return CallNextHookEx(HookID, Code, W, L); 

    } 

    public enum KeyEvents 
    { 
     KeyDown = 0x0100, 
     KeyUp = 0x0101, 
     SKeyDown = 0x0104, 
     SKeyUp = 0x0105 
    } 

    [DllImport("user32.dll")] 
    static public extern short GetKeyState(System.Windows.Forms.Keys nVirtKey); 

    public static bool GetCapslock() 
    { 
     return Convert.ToBoolean(GetKeyState(System.Windows.Forms.Keys.CapsLock)) & true; 
    } 
    public static bool GetNumlock() 
    { 
     return Convert.ToBoolean(GetKeyState(System.Windows.Forms.Keys.NumLock)) & true; 
    } 
    public static bool GetScrollLock() 
    { 
     return Convert.ToBoolean(GetKeyState(System.Windows.Forms.Keys.Scroll)) & true; 
    } 
    public static bool GetShiftPressed() 
    { 
     int state = GetKeyState(System.Windows.Forms.Keys.ShiftKey); 
     if (state > 1 || state < -1) return true; 
     return false; 
    } 
    public static bool GetCtrlPressed() 
    { 
     int state = GetKeyState(System.Windows.Forms.Keys.ControlKey); 
     if (state > 1 || state < -1) return true; 
     return false; 
    } 
    public static bool GetAltPressed() 
    { 
     int state = GetKeyState(System.Windows.Forms.Keys.Menu); 
     if (state > 1 || state < -1) return true; 
     return false; 
    } 
} 

而且在Form1的樣本如何使用它:

using System; 
using System.Windows.Forms; 

namespace KeyboardHook 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      InitializeComponent(); 

      Hook kh = new Hook(true); 
      kh.KeyDown += Kh_KeyDown; 
     } 

     private void Kh_KeyDown(Keys key, bool Shift, bool Ctrl, bool Alt) 
     { 
      MessageBox.Show(key.ToString()); 
     } 

     private void Form1_Load(object sender, EventArgs e) 
     { 

     } 
    } 
} 

我不知道是否有使用類或其他方式也勾鼠標的方法嗎?例如,當我點擊鼠標中間按鈕。或者,如果我滾動鼠標滾輪,然後給我一個消息滾動輪,但輪不是那麼重要更多的按鈕左,右,中間。

+1

檢查此SO [answer](https://stackoverflow.com/a/10818037/4685428) –

回答

2

是的,我用下面的代碼在我的一個項目,它允許直接訪問大多數Windows鼠標事件:

using System; 
using System.Runtime.InteropServices; 

/// <summary> 
///  The CallWndProc hook procedure is an application-defined or library-defined 
///  callback function used with the SetWindowsHookEx function. The HOOKPROC type 
///  defines a pointer to this callback function. CallWndProc is a placeholder for 
///  the application-defined or library-defined function name. 
/// </summary> 
/// <param name="nCode"> 
///  Specifies whether the hook procedure must process the message. 
/// </param> 
/// <param name="wParam"> 
///  Specifies whether the message was sent by the current thread. 
/// </param> 
/// <param name="lParam"> 
///  Pointer to a CWPSTRUCT structure that contains details about the message. 
/// </param> 
/// <returns> 
///  If nCode is less than zero, the hook procedure must return the value returned 
///  by CallNextHookEx. If nCode is greater than or equal to zero, it is highly 
///  recommended that you call CallNextHookEx and return the value it returns; 
///  otherwise, other applications that have installed WH_CALLWNDPROC hooks will 
///  not receive hook notifications and may behave incorrectly as a result. If the 
///  hook procedure does not call CallNextHookEx, the return value should be zero. 
/// </returns> 
internal delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam); 

internal class NativeMethods 
{ 
    /// <summary> 
    ///  The SetWindowsHookEx function installs an application-defined hook 
    ///  procedure into a hook chain. You would install a hook procedure to monitor 
    ///  the system for certain types of events. These events are associated either 
    ///  with a specific thread or with all threads in the same desktop as the 
    ///  calling thread. 
    /// </summary> 
    /// <param name="hookType"> 
    ///  Specifies the type of hook procedure to be installed 
    /// </param> 
    /// <param name="callback">Pointer to the hook procedure.</param> 
    /// <param name="hMod"> 
    ///  Handle to the DLL containing the hook procedure pointed to by the lpfn 
    ///  parameter. The hMod parameter must be set to NULL if the dwThreadId 
    ///  parameter specifies a thread created by the current process and if the 
    ///  hook procedure is within the code associated with the current process. 
    /// </param> 
    /// <param name="dwThreadId"> 
    ///  Specifies the identifier of the thread with which the hook procedure is 
    ///  to be associated. 
    /// </param> 
    /// <returns> 
    ///  If the function succeeds, the return value is the handle to the hook 
    ///  procedure. If the function fails, the return value is 0. 
    /// </returns> 
    [DllImport("user32.dll", CharSet = CharSet.Auto)] 
    public static extern IntPtr SetWindowsHookEx(HookType hookType, 
     HookProc callback, IntPtr hMod, uint dwThreadId); 

    /// <summary> 
    ///  The UnhookWindowsHookEx function removes a hook procedure installed in 
    ///  a hook chain by the SetWindowsHookEx function. 
    /// </summary> 
    /// <param name="hhk">Handle to the hook to be removed.</param> 
    /// <returns> 
    ///  If the function succeeds, the return value is true. 
    ///  If the function fails, the return value is false. 
    /// </returns> 
    [DllImport("user32.dll", CharSet = CharSet.Auto)] 
    public static extern bool UnhookWindowsHookEx(IntPtr hhk); 

    /// <summary> 
    ///  The CallNextHookEx function passes the hook information to the next hook 
    ///  procedure in the current hook chain. A hook procedure can call this 
    ///  function either before or after processing the hook information. 
    /// </summary> 
    /// <param name="idHook">Handle to the current hook.</param> 
    /// <param name="nCode"> 
    ///  Specifies the hook code passed to the current hook procedure. 
    /// </param> 
    /// <param name="wParam"> 
    ///  Specifies the wParam value passed to the current hook procedure. 
    /// </param> 
    /// <param name="lParam"> 
    ///  Specifies the lParam value passed to the current hook procedure. 
    /// </param> 
    /// <returns> 
    ///  This value is returned by the next hook procedure in the chain. 
    /// </returns> 
    [DllImport("user32.dll", CharSet = CharSet.Auto)] 
    public static extern int CallNextHookEx(IntPtr hhk, int nCode, 
     IntPtr wParam, IntPtr lParam); 
} 

internal static class HookCodes 
{ 
    public const int HC_ACTION = 0; 
    public const int HC_GETNEXT = 1; 
    public const int HC_SKIP = 2; 
    public const int HC_NOREMOVE = 3; 
    public const int HC_NOREM = HC_NOREMOVE; 
    public const int HC_SYSMODALON = 4; 
    public const int HC_SYSMODALOFF = 5; 
} 

internal enum HookType 
{ 
    WH_KEYBOARD = 2, 
    WH_MOUSE = 7, 
    WH_KEYBOARD_LL = 13, 
    WH_MOUSE_LL = 14 
} 

[StructLayout(LayoutKind.Sequential)] 
internal class POINT 
{ 
    public int x; 
    public int y; 
} 

/// <summary> 
///  The MSLLHOOKSTRUCT structure contains information about a low-level keyboard 
///  input event. 
/// </summary> 
[StructLayout(LayoutKind.Sequential)] 
internal struct MOUSEHOOKSTRUCT 
{ 
    public POINT pt; // The x and y coordinates in screen coordinates 
    public int hwnd; // Handle to the window that'll receive the mouse message 
    public int wHitTestCode; 
    public int dwExtraInfo; 
} 

/// <summary> 
///  The MOUSEHOOKSTRUCT structure contains information about a mouse event passed 
///  to a WH_MOUSE hook procedure, MouseProc. 
/// </summary> 
[StructLayout(LayoutKind.Sequential)] 
internal struct MSLLHOOKSTRUCT 
{ 
    public POINT pt; // The x and y coordinates in screen coordinates. 
    public int mouseData; // The mouse wheel and button info. 
    public int flags; 
    public int time; // Specifies the time stamp for this message. 
    public IntPtr dwExtraInfo; 
} 

internal enum MouseMessage 
{ 
    WM_MOUSEMOVE = 0x0200, 
    WM_LBUTTONDOWN = 0x0201, 
    WM_LBUTTONUP = 0x0202, 
    WM_LBUTTONDBLCLK = 0x0203, 
    WM_RBUTTONDOWN = 0x0204, 
    WM_RBUTTONUP = 0x0205, 
    WM_RBUTTONDBLCLK = 0x0206, 
    WM_MBUTTONDOWN = 0x0207, 
    WM_MBUTTONUP = 0x0208, 
    WM_MBUTTONDBLCLK = 0x0209, 

    WM_MOUSEWHEEL = 0x020A, 
    WM_MOUSEHWHEEL = 0x020E, 

    WM_NCMOUSEMOVE = 0x00A0, 
    WM_NCLBUTTONDOWN = 0x00A1, 
    WM_NCLBUTTONUP = 0x00A2, 
    WM_NCLBUTTONDBLCLK = 0x00A3, 
    WM_NCRBUTTONDOWN = 0x00A4, 
    WM_NCRBUTTONUP = 0x00A5, 
    WM_NCRBUTTONDBLCLK = 0x00A6, 
    WM_NCMBUTTONDOWN = 0x00A7, 
    WM_NCMBUTTONUP = 0x00A8, 
    WM_NCMBUTTONDBLCLK = 0x00A9 
} 

/// <summary> 
///  The structure contains information about a low-level keyboard input event. 
/// </summary> 
[StructLayout(LayoutKind.Sequential)] 
internal struct KBDLLHOOKSTRUCT 
{ 
    public int vkCode; // Specifies a virtual-key code 
    public int scanCode; // Specifies a hardware scan code for the key 
    public int flags; 
    public int time; // Specifies the time stamp for this message 
    public int dwExtraInfo; 
} 

internal enum KeyboardMessage 
{ 
    WM_KEYDOWN = 0x0100, 
    WM_KEYUP = 0x0101, 
    WM_SYSKEYDOWN = 0x0104, 
    WM_SYSKEYUP = 0x0105 
} 

要使用它,你必須註冊鼠標鉤子。 LowLevelMouseProc是回調。每次發生新的鼠標事件時都會執行此方法。

private void SetUpHook() 
{ 
    Logger.Debug("Setting up global mouse hook"); 

    // Create an instance of HookProc. 
    _globalLlMouseHookCallback = LowLevelMouseProc; 

    _hGlobalLlMouseHook = NativeMethods.SetWindowsHookEx(
     HookType.WH_MOUSE_LL, 
     _globalLlMouseHookCallback, 
     Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 
     0); 

    if (_hGlobalLlMouseHook == IntPtr.Zero) 
    { 
     Logger.Fatal("Unable to set global mouse hook"); 
     throw new Win32Exception("Unable to set MouseHook"); 
    } 
} 

要清除鼠標鉤子:

private void ClearHook() 
{ 
    Logger.Debug("Deleting global mouse hook"); 

    if (_hGlobalLlMouseHook != IntPtr.Zero) 
    { 
     // Unhook the low-level mouse hook 
     if (!NativeMethods.UnhookWindowsHookEx(_hGlobalLlMouseHook)) 
      throw new Win32Exception("Unable to clear MouseHoo;"); 

     _hGlobalLlMouseHook = IntPtr.Zero; 
    } 
} 

最後但並非最不重要的LowLevelMouseProc的例子,回調可以使用攔截鼠標事件:

public int LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam) 
{ 
    if (nCode >= 0) 
    { 
     // Get the mouse WM from the wParam parameter 
     var wmMouse = (MouseMessage) wParam; 
     if (wmMouse == MouseMessage.WM_LBUTTONDOWN && LeftButtonState == ButtonState.Released) 
     { 
      Logger.Debug("Left Mouse down"); 
     } 
     if (wmMouse == MouseMessage.WM_LBUTTONUP && LeftButtonState == ButtonState.Down) 
     { 
      Logger.Debug("Left Mouse up"); 
     } 

     if (wmMouse == MouseMessage.WM_RBUTTONDOWN && RightButtonState == ButtonState.Released) 
     { 
      Logger.Debug("Right Mouse down"); 
     } 
     if (wmMouse == MouseMessage.WM_RBUTTONUP && RightButtonState == ButtonState.Down) 
     { 
      Logger.Debug("Right Mouse up"); 
     } 
    } 

    // Pass the hook information to the next hook procedure in chain 
    return NativeMethods.CallNextHookEx(_hGlobalLlMouseHook, nCode, wParam, lParam); 
} 

與所有直接窗口調用,代碼獲得不必要的長。但你唯一需要做的就是致電SetUpHook並提供你自己的LowLevelMouseProc版本。

編輯:有更短的版本來做到這一點。但是這種方法可以讓你捕捉全局鼠標事件。不僅僅是發佈到你的窗口的事件。所有的鼠標事件,全系統將被傳送到LowLevelMouseProc

+0

偉大的代碼。有一種方法可以捕捉實際的菜單選項點擊,以防鼠標右鍵單擊。就像標準的剪切/複製/粘貼/刪除/屬性等一樣,? –

+0

我不知道,至少在上面發佈的示例中是不可能的。 – Iqon