Files
wg_cpso/CaeGlobals/MouseHook.cs
2026-03-25 18:20:24 +08:00

191 lines
7.7 KiB
C#

using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
namespace CaeGlobals
{
/// <summary>
/// Class for intercepting low level Windows mouse hooks.
/// /// https://github.com/rvknth043/Global-Low-Level-Key-Board-And-Mouse-Hook
/// </summary>
public class MouseHook
{
/// <summary>
/// Internal callback processing function
/// </summary>
private delegate IntPtr MouseHookHandler(int nCode, IntPtr wParam, IntPtr lParam);
private MouseHookHandler hookHandler;
private static long _lastClickTime = 0;
private static Point _lastClickPosition;
private static readonly int DoubleClickTime = SystemInformation.DoubleClickTime;
private static readonly Size DoubleClickSize = SystemInformation.DoubleClickSize;
/// <summary>
/// Function to be called when defined even occurs
/// </summary>
/// <param name="mouseStruct">MSLLHOOKSTRUCT mouse structure</param>
public delegate bool MouseHookCallback(MSLLHOOKSTRUCT mouseStruct);
#region Events
public event MouseHookCallback LeftButtonDown;
public event MouseHookCallback LeftButtonUp;
public event MouseHookCallback RightButtonDown;
public event MouseHookCallback RightButtonUp;
public event MouseHookCallback MouseMove;
public event MouseHookCallback MouseWheel;
public event MouseHookCallback DoubleClick;
public event MouseHookCallback MiddleButtonDown;
public event MouseHookCallback MiddleButtonUp;
#endregion
/// <summary>
/// Low level mouse hook's ID
/// </summary>
private IntPtr hookID = IntPtr.Zero;
/// <summary>
/// Install low level mouse hook
/// </summary>
/// <param name="mouseHookCallbackFunc">Callback function</param>
public void Install()
{
hookHandler = HookFunc;
hookID = SetHook(hookHandler);
}
/// <summary>
/// Remove low level mouse hook
/// </summary>
public void Uninstall()
{
if (hookID == IntPtr.Zero)
return;
UnhookWindowsHookEx(hookID);
hookID = IntPtr.Zero;
}
/// <summary>
/// Destructor. Unhook current hook
/// </summary>
~MouseHook()
{
Uninstall();
}
/// <summary>
/// Sets hook and assigns its ID for tracking
/// </summary>
/// <param name="proc">Internal callback function</param>
/// <returns>Hook ID</returns>
private IntPtr SetHook(MouseHookHandler proc)
{
using (ProcessModule module = Process.GetCurrentProcess().MainModule)
return SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle(module.ModuleName), 0);
}
/// <summary>
/// Callback function
/// </summary>
private IntPtr HookFunc(int nCode, IntPtr wParam, IntPtr lParam)
{
bool callNextHook = true;
// parse system messages
if (nCode >= 0)
{
if (MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
{
var mouseStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
var currentTime = Environment.TickCount;
var currentPosition = new Point(mouseStruct.pt.x, mouseStruct.pt.y);
//
if (currentTime - _lastClickTime <= DoubleClickTime &&
Math.Abs(currentPosition.X - _lastClickPosition.X) <= DoubleClickSize.Width &&
Math.Abs(currentPosition.Y - _lastClickPosition.Y) <= DoubleClickSize.Height &&
DoubleClick != null)
{
callNextHook = DoubleClick(mouseStruct);
}
else if (LeftButtonDown != null) callNextHook = LeftButtonDown(mouseStruct);
//
_lastClickTime = currentTime;
_lastClickPosition = currentPosition;
}
if (MouseMessages.WM_LBUTTONUP == (MouseMessages)wParam)
if (LeftButtonUp != null)
callNextHook = LeftButtonUp((MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)));
if (MouseMessages.WM_RBUTTONDOWN == (MouseMessages)wParam)
if (RightButtonDown != null)
callNextHook = RightButtonDown((MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)));
if (MouseMessages.WM_RBUTTONUP == (MouseMessages)wParam)
if (RightButtonUp != null)
callNextHook = RightButtonUp((MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)));
if (MouseMessages.WM_MOUSEMOVE == (MouseMessages)wParam)
if (MouseMove != null)
callNextHook = MouseMove((MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)));
if (MouseMessages.WM_MOUSEWHEEL == (MouseMessages)wParam)
if (MouseWheel != null)
callNextHook = MouseWheel((MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)));
if (MouseMessages.WM_MBUTTONDOWN == (MouseMessages)wParam)
if (MiddleButtonDown != null)
callNextHook = MiddleButtonDown((MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)));
if (MouseMessages.WM_MBUTTONUP == (MouseMessages)wParam)
if (MiddleButtonUp != null)
callNextHook = MiddleButtonUp((MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)));
}
//
if (callNextHook) return CallNextHookEx(hookID, nCode, wParam, lParam);
return (IntPtr)1;
}
#region WinAPI
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,
WM_LBUTTONDBLCLK = 0x0203,
WM_MBUTTONDOWN = 0x0207,
WM_MBUTTONUP = 0x0208
}
[StructLayout(LayoutKind.Sequential)]
public struct 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;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
MouseHookHandler lpfn, IntPtr hMod, uint dwThreadId);
[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)]
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);
#endregion
}
}