laitimes

How to use the mouse hook in C# to record and play back the mouse events and sequences of human operations

author:Night curtain 9507927

今天大年初二,相信大家听说过WINDOWS系统下的按键精灵一类的软件,会录制一段人类操作鼠标的操作,然后自动重复,减轻人类重复工作的工作量,这也算是很古早时代的算是自动化操作的一种方式吧。

这次采用C#的鼠标钩åæ¥å®žçŽ°è¿™ä¸ªæ–¹æ¡ˆï¼Œä¹‹æ‰€ä»¥å«é¼ æ ‡é’©åæ˜¯å› ä¸ºæ—©æœŸä¸€æ‰¹äººç¿»è¯‘这个HOOK就翻译成钩åï¼ŒçŽ°åœ¨çš„人听的云里雾里,其实你可以理解为就像你可以使用现实生活ä¸çš„é’©åæ¥æ•èŽ·æˆ–挂住某些物体一样,编程ä¸çš„é’©åå¯ä»¥å¸®åŠ©ä½ "捕获" 或"挂住"某些事件或函数。

How to use the mouse hook in C# to record and play back the mouse events and sequences of human operations

举一个简单的例åï¼Œå‡è®¾ä½ åœ¨ä¸€ä¸ªå¤§åž‹è´ç‰©ä¸å¿ƒï¼Œä½ æƒ³çŸ¥é“有多少人进入了这个è´ç‰©ä¸å¿ƒã€‚为æ¤ï¼Œä½ å¯ä»¥åœ¨å…¥å£å¤„放一个人,每当有人进入è´ç‰©ä¸å¿ƒæ—¶ï¼Œè¿™ä¸ªäººå°±ç”¨è®°äº‹æœ¬è®°ä¸‹ä¸€ç¬”。这个人就像是一个"钩å",他"捕获" 了进入è´ç‰©ä¸å¿ƒçš„事件,并执行了一些操作(即记笔记)。那既然有鼠标钩åå°±è‚¯å®šä¹Ÿæœ‰å…¶å®ƒé’©åï¼Œä¾‹å¦‚键盘钩åã€WINDOWS消息钩åç‰ç‰ï¼Œåƒé”®ç›˜é’©åå°±å¾ˆå¥½ç†è§£ï¼Œå°±æ˜¯è®°å½•é”®ç›˜çš„输入信息,更早期的时候,用这个方法是可以捕获到类似游戏登录账号密码、QQ账号密码一类的,现在这些输入文本框都升级了,在2003年以前都是获取到的。好了,今天我们讲的是鼠标钩åï¼Œå›žå½’主题。

How to use the mouse hook in C# to record and play back the mouse events and sequences of human operations

那要做一个类似按键精灵类软件,那么首先得采集到鼠标的相应事件,如左键单击,右键单击、鼠标移动ç‰ç‰ï¼Œè¿˜æœ‰æŒ‰é”®ç§»åŠ¨ç‰äº‹ä»¶çš„时间间隔顺序也需要采集到,否则没有时间间隔的操作一来不像真人操作,二来很多事情是需要一定延时才能æ£å¸¸çš„操作,例如有些软件双击后需要1,2秒才能打开,就得延时这1,2秒。好了,废话不多说,直接开干。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

public class RecordForm : Form
{
    private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);

    [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("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);

    private const int WH_MOUSE_LL = 14;
    private LowLevelMouseProc _proc;
    private IntPtr _hookID = IntPtr.Zero;

    private List<MouseEvent> mouseEvents = new List<MouseEvent>();
    private Stopwatch stopwatch = new Stopwatch();

    public RecordForm()
    {
        _proc = HookCallback;
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        _hookID = SetHook(_proc);
    }

    protected override void OnClosed(EventArgs e)
    {
        base.OnClosed(e);
        UnhookWindowsHookEx(_hookID);
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
        base.OnMouseDown(e);
        if (e.Button == MouseButtons.Right)
        {
            stopwatch.Reset();
            mouseEvents.Clear();
            stopwatch.Start();
        }
        else if (e.Button == MouseButtons.Left)
        {
            stopwatch.Stop();
            Playback();
        }
    }

    private IntPtr SetHook(LowLevelMouseProc proc)
    {
        using (var curProcess = System.Diagnostics.Process.GetCurrentProcess())
        using (var curModule = curProcess.MainModule)
        {
            return SetWindowsHookEx(WH_MOUSE_LL, proc, Marshal.GetHINSTANCE(curModule.ModuleName), 0);
        }
    }

    private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && stopwatch.IsRunning)
        {
            MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
            mouseEvents.Add(new MouseEvent(hookStruct.pt, (MouseMessages)wParam, stopwatch.ElapsedMilliseconds));
        }
        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }

    private void Playback()
    {
        long previousTime = 0;
        foreach (var mouseEvent in mouseEvents)
        {
            Thread.Sleep((int)(mouseEvent.Time - previousTime));
            previousTime = mouseEvent.Time;

            INPUT input = new INPUT();
            input.type = INPUT_MOUSE;
            input.U.mi.dx = mouseEvent.Location.X * (65536 / Screen.PrimaryScreen.Bounds.Width); // x being coord in pixels
            input.U.mi.dy = mouseEvent.Location.Y * (65536 / Screen.PrimaryScreen.Bounds.Height); // y being coord in pixels
            input.U.mi.mouseData = 0;

            switch (mouseEvent.Message)
            {
                case MouseMessages.WM_LBUTTONDOWN:
                    input.U.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
                    break;
                case MouseMessages.WM_LBUTTONUP:
                    input.U.mi.dwFlags = MOUSEEVENTF_LEFTUP;
                    break;
                case MouseMessages.WM_MOUSEMOVE:
                    input.U.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
                    break;
                case MouseMessages.WM_MOUSEWHEEL:
                    input.U.mi.dwFlags = MOUSEEVENTF_WHEEL;
                    break;
                case MouseMessages.WM_RBUTTONDOWN:
                    input.U.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
                    break;
                case MouseMessages.WM_RBUTTONUP:
                    input.U.mi.dwFlags = MOUSEEVENTF_RIGHTUP;
                    break;
            }

            SendInput(1, new INPUT[] { input }, Marshal.SizeOf(typeof(INPUT)));
        }
    }

    private const int INPUT_MOUSE = 0;

    private const uint MOUSEEVENTF_MOVE = 0x0001;
    private const uint MOUSEEVENTF_LEFTDOWN = 0x0002;
    private const uint MOUSEEVENTF_LEFTUP = 0x0004;
    private const uint MOUSEEVENTF_RIGHTDOWN = 0x0008;
    private const uint MOUSEEVENTF_RIGHTUP = 0x0010;
    private const uint MOUSEEVENTF_ABSOLUTE = 0x8000;
    private const uint MOUSEEVENTF_WHEEL = 0x0800;

    private struct INPUT
    {
        public int type;
        public InputUnion U;
    }

    [StructLayout(LayoutKind.Explicit)]
    private struct InputUnion
    {
        [FieldOffset(0)]
        public MOUSEINPUT mi;
    }

    private struct POINT
    {
        public int x;
        public int y;
    }

    private struct MOUSEINPUT
    {
        public int dx;
        public int dy;
        public uint mouseData;
        public uint dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

    private struct MSLLHOOKSTRUCT
    {
        public POINT pt;
        public uint mouseData;
        public uint flags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

    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_RBUTTONDBLCLK = 0x0206
    }

    private class MouseEvent
    {
        public Point Location { get; }
        public MouseMessages Message { get; }
        public long Time { get; }

        public MouseEvent(Point location, MouseMessages message, long time)
        {
            Location = location;
            Message = message;
            Time = time;
        }
    }

    [STAThread]
    public static void Main()
    {
        Application.Run(new RecordForm());
    }
}
           

今天没加注释,代码都很简单,代码即注释!如果你对鼠标钩åä¸ç†Ÿæ‚‰ï¼Œå»ºè®®ä½ æŠŠé’©åé‚£éƒ¨ä»½ä»£ç æ‰£å‡ºæ¥ï¼Œç‹¬ç«‹å°è£…成一个类,这样动手做一下很容易就明白这钩ååŽŸç†äº†ï¼Œéžå¸¸çš„简单,对了,因为用到了鼠标钩åè¿™ç±»ç³»ç»ŸAPI,不同的WINDOWS系统要求可能不一样 ,有些WINDOWS系统可能需要你的管理员权限,那你就右键程序管理员权限执行即可。

这些不同的钩ååœ¨WINDOWS系统ä¸å¯ä»¥åšè®¸è®¸å¤šå¤šçš„事情,例如:拦截操作系统的消息,比如鼠标点击或键盘输入、修改或增强系统函数或库函数的行为、监控或记录系统或应用程序的行为。当然你还可以用在某些邪恶的目的ä¸ï¼Œæ¯”如创建键盘记录器或其他恶意软件,请最大的发挥你们的想象空间,哈哈哈,格局打开会有很多新思路。

#ç1/4c ̈#