21. Dezember 2008

Auf GlobaleEvents mit dem .NET Framework reagieren

Globale Events können mit dem .NET Framework über die Win32-Funktion SetWindowsHookEx behandelt werden. Allerdings lassen sich nur die beiden low-level Hooks WH_KEYBOARD_LL und WH_MOUSE_LL behandeln.

Win32-Funktionen:

public class Win32Helper
{


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


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


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


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



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



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


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

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


public enum WindowsHookCodes : int
{

WH_KEYBOARD = 2,
WH_MOUSE = 7,
WH_KEYBOARD_LL = 13,
WH_MOUSE_LL = 14
}
}


Die Klasse HookHelper kapselt den Zugriff auf die Win32-Funktionen und stellt ein Event bereit, auf das sich andere Klassen registrieren können.

  public class HookHelper
{
private static Win32Helper.LowLevelMouseProc _proc = LowLevelMouseHookProc;
private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

public delegate void GlobalMouseListener(int code, IntPtr wParam, Win32Helper.MSLLHOOKSTRUCT hookStruct);

public static event GlobalMouseListener GlobalMouseListenerEvent;

private static IntPtr hGlobalMouseHook;

public static bool StartGlobalMouseHook()
{
using (Process currentProcess = Process.GetCurrentProcess())
{
using (ProcessModule currentModule = currentProcess.MainModule)
{
hGlobalMouseHook = Win32Helper.SetWindowsHookEx((int)Win32Helper.WindowsHookCodes.WH_MOUSE_LL,
_proc,
Win32Helper.GetModuleHandle(currentModule.ModuleName),
0);
if (hGlobalMouseHook.ToInt32() == 0)
{
log.Error("SetWindowsHookEx Failed");
return false;
}
log.Debug("SetWindowsHookEx called for " + hGlobalMouseHook + " with sucess");
return true;
}
}

}

/// <summary>
/// Stoppt den globalen Maus-Hook.
/// </summary>
public static void StopGlobalMouseHook()
{

bool b = Win32Helper.UnhookWindowsHookEx(hGlobalMouseHook);
if (b)
{
log.Debug("StopGlobalMouseHook called for " + hGlobalMouseHook + " with sucess");
}
else
{
log.Error("StopGlobalMouseHook called for " + hGlobalMouseHook + " failed");
}
}

/// <summary>
/// Diese Methode wird im Falle gerufen im Falle des MouseHooks.
/// Sorgt dafuer, dasd die EventListener von GlobalMouseListenerEvent gerufen werden.
/// </summary>
/// <param name="nCode"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
private static IntPtr LowLevelMouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{

Win32Helper.MSLLHOOKSTRUCT hookStruct = (Win32Helper.MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32Helper.MSLLHOOKSTRUCT));

if (GlobalMouseListenerEvent.GetInvocationList().Length > 0)
{
GlobalMouseListenerEvent(nCode, wParam, hookStruct);
}

return Win32Helper.CallNextHookEx(hGlobalMouseHook, nCode, wParam, lParam);
}

}


In einem Form kann der HookHelper wie folgt verwendet werden:


public MainForm()
{
InitializeComponent();

HookHelper.GlobalMouseListenerEvent += new HookHelper.GlobalMouseListener(MyGlobalMouseListener);
HookHelper.StartGlobalMouseHook();
...
}


public void MyGlobalMouseListener(int code, IntPtr wParam, Win32Helper.MSLLHOOKSTRUCT hookStruct)
{
if (code >= 0 && Win32Helper.MouseMessages.WM_LBUTTONDOWN == (Win32Helper.MouseMessages)wParam)
{
System.Diagnostics.Trace.WriteLine(hookStruct.pt.x + ", " + hookStruct.pt.y);
}
}

Keine Kommentare: