2012-07-23 5 views
24

Ho il seguente codice che ho ottenuto da qualche parte per acquisire gli eventi del mouse. L'ho modificato e ho creato un gestore di eventi in modo da potermi iscrivere. Gli eventi del mouse vengono acquisiti correttamente. Ma non licenzia mai l'event-handler. Qualcuno può capire che cosa non va con il codice?Gestore di eventi globali del mouse

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); 


} 

Mi iscrivo in questo modo.

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

Funzione ricezione dell'evento.

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

Aggiornamento: ho messo insieme il codice di lavoro in un open source nuget package for user action hooks.

+1

Questo non può funzionare in un'app in modalità console, il programma deve pompare un ciclo di messaggi. Application.Run() è richiesto. –

+0

Uso effettivamente il codice precedente all'interno della mia applicazione WPF. Chiaro la classe MouseHook dal metodo Onstartup di App.cs. – justcoding124

+0

Per tutti se si ritiene che ciò causi il trascinamento del mouse, eseguirlo in un processo separato separato e utilizzare thread separati per gestire gli eventi. – justcoding124

risposta

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

Questo codice avrà esito negativo quando si esegue su .NET 4 su una versione di Windows precedente a Windows 8. CLR non simula più le maniglie dei moduli non gestite per gli assembly gestiti. Non è possibile rilevare questo errore nel codice perché manca la verifica degli errori richiesta. Sia su GetModuleHandle e SetWindowsHookEx. Mai saltare il controllo degli errori quando si esegue il pinvoke, il winapi non genera eccezioni. Controlla se restituiscono IntPtr.Zero e semplicemente lancia una Win32Exception quando lo fanno.

La correzione è semplice, SetWindowsHookEx() richiede un handle di modulo valido ma in realtà non lo utilizza quando si imposta un hook di mouse di basso livello. Quindi qualsiasi handle lo farà, è possibile passare l'handle per user32.dll, sempre caricato in un'applicazione .NET. Correzione:

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

Grazie, ma ho modificato il codice con la correzione, utilizzato per mostrare le coordinate quando utilizzo Console.WriteLine (hookStruct.pt.x + "," + hookStruct.pt .y) all'interno della funzione HookCallback Ora non funziona – justcoding124

+0

Ehm, aspetta, questo è iniziato con "non funziona". In realtà funzionava? Puoi almeno documentare la versione di .NET e Windows che stai utilizzando? vedere le eccezioni della prima uscita nella finestra Output? –

+0

Sì, stavo dando le coordinate del mouse quando lavoravo ieri.In realtà ho controllato se l'evento è stato attivato utilizzando il tracciamento del percorso di debug. Sì lo fa! Ma Console.WriteLine (" qualcosa ") non mostra alcun output.Penso che la console.writeline non funzioni correttamente.Strano – justcoding124

8

Io ho semplicemente copiato il codice in una forma semplice di Windows e il suo funzionamento come descritto dovrebbe. Come stai usando esattamente? Dove stai iniziando e allegando l'evento?

E per completezza questo è il codice ho finito con - ha iniziato da una forma semplice modello di 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

Se desidero conoscere le coordinate di mouseClicks, cosa devo fare? –

+0

Funziona per tutti gli eventi dei pulsanti del mouse, eccetto il quinto pulsante. In realtà il 4 ° e il 5 ° pulsante restituiscono lo stesso codice. – Masum

+0

L'ho appena fatto. pubblico statico int x = 0; // va in Program.cs e MyConsoleApp.Program.x = hookStruct.pt.x; // trovato in HookCallback –