2012-07-23 150 views
24

我有以下代碼,我從某處捕獲鼠標事件。我修改了它並創建了一個事件處理程序,以便我可以訂閱它。鼠標事件被正確捕獲。但它永遠不會觸發事件處理程序。任何人都可以弄清楚代碼有什麼問題嗎?全球鼠標事件處理程序

public static class MouseHook 

{ 
    public static event EventHandler MouseAction = delegate { }; 

    public static void Start() 
    { 
     _hookID = SetHook(_proc); 


    } 
    public static void stop() 
    { 
     UnhookWindowsHookEx(_hookID); 
    } 

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

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

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

    private static IntPtr HookCallback(
     int nCode, IntPtr wParam, IntPtr lParam) 
    { 
     if (nCode >= 0 && MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam) 
     { 
      MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)); 
      MouseAction(null,new EventArgs()); 
     } 
     return CallNextHookEx(_hookID, nCode, wParam, lParam); 
    } 

    private const int WH_MOUSE_LL = 14; 

    private enum MouseMessages 
    { 
     WM_LBUTTONDOWN = 0x0201, 
     WM_LBUTTONUP = 0x0202, 
     WM_MOUSEMOVE = 0x0200, 
     WM_MOUSEWHEEL = 0x020A, 
     WM_RBUTTONDOWN = 0x0204, 
     WM_RBUTTONUP = 0x0205 
    } 

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

    [StructLayout(LayoutKind.Sequential)] 
    private struct MSLLHOOKSTRUCT 
    { 
     public POINT pt; 
     public uint mouseData; 
     public uint flags; 
     public uint time; 
     public IntPtr dwExtraInfo; 
    } 

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern IntPtr SetWindowsHookEx(int idHook, 
     LowLevelMouseProc 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); 


} 

我這樣訂閱它。

MouseHook.Start(); 
    MouseHook.MouseAction += new EventHandler(Event); 

接收事件的函數。

private void Event(object sender, EventArgs e) { Console.WriteLine("Left mouse click!"); } 

更新: 我放在一起工作代碼爲open source nuget package for user action hooks.

+1

這不能在控制檯模式應用程序中工作,您的程序必須泵送消息循環。 Application.Run()是必需的。 – 2012-07-23 07:51:50

+0

我實際上在我的WPF應用程序中使用上面的代碼。我從App.cs的Onstartup方法調用MouseHook類。 – justcoding124 2012-07-23 14:58:05

+0

對於所有感覺這會導致鼠標拖動的情況,請在單獨的提升進程中運行此操作,並使用單獨的線程來處理事件。 – justcoding124 2013-10-24 02:53:08

回答

20
 return SetWindowsHookEx(WH_MOUSE_LL, proc, 
      GetModuleHandle(curModule.ModuleName), 0); 

,當你在Windows版本上.NET 4中運行該代碼將失敗早於Windows 8.CLR不再模擬託管程序集的非託管模塊句柄。您無法在代碼中檢測到此故障,因爲它缺少所需的錯誤檢查。在GetModuleHandle和SetWindowsHookEx上。 winapi不會拋出異常,切勿錯過錯誤檢查。檢查它們是否返回IntPtr.Zero,並在它們執行時簡單地拋出一個Win32Exception。

修復很簡單,SetWindowsHookEx()需要一個有效的模塊句柄,但實際上在設置低級別的鼠標掛鉤時並未使用它。所以任何句柄都可以,你可以傳遞user32.dll的句柄,並且總是加載到.NET應用程序中。修復:

IntPtr hook = SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle("user32"), 0); 
    if (hook == IntPtr.Zero) throw new System.ComponentModel.Win32Exception(); 
    return hook; 
+0

謝謝。但我修改了代碼與修復。它用來顯示座標,當我使用Console.WriteLine(hookStruct.pt.x +「,」+ hookStruct.pt .y)在HookCallback函數中,現在它不能正常工作 – justcoding124 2012-07-23 15:30:33

+0

Erm,等等,這開始於「它不工作」,它實際上在工作嗎?你至少能記錄你使用的.NET和Windows的版本嗎?在輸出窗口中看到一次性異常嗎? – 2012-07-23 15:38:47

+0

是的,它在昨天工作時給出了鼠標座標,實際上我檢查了是否使用調試路徑跟蹤來觸發事件,但是它確實!但是Console.WriteLine(「 「)不顯示任何輸出。我認爲console.writeline不能正常工作。奇怪的 – justcoding124 2012-07-23 15:42:58

8

像你描述它應該我只是複製你的代碼放到一個簡單的Windows窗體和工作。你如何準確地使用它?你在哪裏開始並附加活動?

而對於完整性的考慮,這是我最終的代碼 - 從簡單的C#表單模板開始

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

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

      MouseHook.Start(); 
      MouseHook.MouseAction += new EventHandler(Event); 
     } 

     private void Event(object sender, EventArgs e) { Console.WriteLine("Left mouse click!"); } 
    } 

    public static class MouseHook 
    { 
     public static event EventHandler MouseAction = delegate { }; 

     public static void Start() 
     { 
      _hookID = SetHook(_proc); 


     } 
     public static void stop() 
     { 
      UnhookWindowsHookEx(_hookID); 
     } 

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

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

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

     private static IntPtr HookCallback(
      int nCode, IntPtr wParam, IntPtr lParam) 
     { 
      if (nCode >= 0 && MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam) 
      { 
       MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)); 
       MouseAction(null, new EventArgs()); 
      } 
      return CallNextHookEx(_hookID, nCode, wParam, lParam); 
     } 

     private const int WH_MOUSE_LL = 14; 

     private enum MouseMessages 
     { 
      WM_LBUTTONDOWN = 0x0201, 
      WM_LBUTTONUP = 0x0202, 
      WM_MOUSEMOVE = 0x0200, 
      WM_MOUSEWHEEL = 0x020A, 
      WM_RBUTTONDOWN = 0x0204, 
      WM_RBUTTONUP = 0x0205 
     } 

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

     [StructLayout(LayoutKind.Sequential)] 
     private struct MSLLHOOKSTRUCT 
     { 
      public POINT pt; 
      public uint mouseData; 
      public uint flags; 
      public uint time; 
      public IntPtr dwExtraInfo; 
     } 

     [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     private static extern IntPtr SetWindowsHookEx(int idHook, 
      LowLevelMouseProc 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); 


    } 
} 
+0

如果我想知道mouseClicks的座標,我該怎麼做? – 2014-04-29 06:50:56

+0

適用於除第5個按鈕以外的所有鼠標按鈕事件。其實第4和第5個按鈕返回相同的代碼。 – Masum 2015-12-22 06:03:57

+0

我只是做了這個。 public static int x = 0; //進入Program.cs和 MyConsoleApp.Program.x = hookStruct.pt.x; //在HookCallback中找到 – 2016-04-20 19:22:30