• WinFrom、C# 学习记录五 开发一个鼠标自动点击小软件


    一、说明

            经常会被问到需要点击软件的,主要都是玩游戏的盆友,但是也有其它用途的。所以简单弄了一个,打算每当有时间,有需求,就加一些小功能。

            这里主要是要记录一下相关开发工作,也记录一些使用/更新的信息。

    二、更新记录

    1、版本说明

            【2022/08/22】版本v1.0(初始版本,主要包含间隔点击模式、识别图片点击模式)

    2、下载地址

    基于winform/c#/opencv实现的windows下使用的自动鼠标点击小软件-桌面系统文档类资源-CSDN下载【2022/08/22】版本v1.0【功能描述】目前实现两种点击模式。间隔模式:一句话描述,更多下载资源、学习资料请访问CSDN下载频道.https://download.csdn.net/download/bashendixie5/86406516

    三、功能描述

    1、点击模式

            目前实现两种点击模式(仅支持鼠标左键单击)。

    (1)间隔模式

            一句话描述,按设置好的间隔时间鼠标左键单击用户设定的位置。

    (2)图片模式

            一句话描述,用户截图想要识别的图片,上传到软件,然后软件按设置好的间隔时间识别屏幕是否出现了用户上传的图片,如果识别到了,则鼠标左键单击用户设定的位置。

             后面会增加一些点击模式,比如文字识别,颜色识别等。

    2、基础设置

            主要设置

    四、基础源码简述

    1、枚举/实体

    1. internal enum ClickModes
    2. {
    3. /// <summary>
    4. /// 间隔模式
    5. /// </summary>
    6. ClickByStep,
    7. /// <summary>
    8. /// 根据图片点击
    9. /// </summary>
    10. ClickByPictures,
    11. /// <summary>
    12. /// 根据文字点击
    13. /// </summary>
    14. ClickByText,
    15. /// <summary>
    16. /// 根据颜色点击
    17. /// </summary>
    18. ClickByColor,
    19. /// <summary>
    20. /// 自定义的点击
    21. /// </summary>
    22. ClickByCustomize
    23. }
    1. public enum SettingModes
    2. {
    3. NotSettingState,
    4. /// <summary>
    5. /// 间隔点击模式的点击位置设置
    6. /// </summary>
    7. ClickByStep_PositionOne,
    8. /// <summary>
    9. /// 根据图片的点击位置设置
    10. /// </summary>
    11. ClickByPictures_PositionOne,
    12. /// <summary>
    13. /// 根据文字的点击位置设置
    14. /// </summary>
    15. ClickByText_PositionOne,
    16. /// <summary>
    17. /// 根据颜色的点击位置设置
    18. /// </summary>
    19. ClickByColor_PositionOne,
    20. /// <summary>
    21. /// 根据自定义的点击设置
    22. /// </summary>
    23. ClickByCustomize_PositionOne,
    24. ClickByCustomize_PositionTwo,
    25. ClickByCustomize_PositionThree,
    26. ClickByCustomize_PositionFour,
    27. ClickByCustomize_PositionFive,
    28. ClickByCustomize_PositionSix,
    29. }

    2、windows api

    1. internal class Win32Api
    2. {
    3. [StructLayout(LayoutKind.Sequential)]
    4. public class POINT
    5. {
    6. public int x;
    7. public int y;
    8. }
    9. [StructLayout(LayoutKind.Sequential)]
    10. public class MouseHookStruct
    11. {
    12. public POINT pt;
    13. public int hwnd;
    14. public int wHitTestCode;
    15. public int dwExtraInfo;
    16. }
    17. public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
    18. //安装钩子
    19. [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    20. public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
    21. //卸载钩子
    22. [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    23. public static extern bool UnhookWindowsHookEx(int idHook);
    24. //调用下一个钩子
    25. [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    26. public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);
    27. [DllImport("User32")]
    28. public extern static void SetCursorPos(int x, int y);
    29. //定义快捷键
    30. [DllImport("user32.dll", SetLastError = true)]
    31. public static extern bool RegisterHotKey(IntPtr hWnd, int id, KeyModifiers fsModifiers, int vk);
    32. [Flags()]
    33. public enum KeyModifiers
    34. {
    35. None = 0,
    36. Alt = 1,
    37. Ctrl = 2,
    38. Shift = 4,
    39. WindowsKey = 8
    40. }
    41. /// <summary>
    42. /// 取消注册热键
    43. /// </summary>
    44. /// <param name="hWnd">要取消热键的窗口的句柄</param>
    45. /// <param name="id">要取消热键的ID</param>
    46. /// <returns></returns>
    47. [DllImport("user32.dll", SetLastError = true)]
    48. public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
    49. /// <summary>
    50. /// 向全局原子表添加一个字符串,并返回这个字符串的唯一标识符,成功则返回值为新创建的原子ID,失败返回0
    51. /// </summary>
    52. /// <param name="lpString"></param>
    53. /// <returns></returns>
    54. [DllImport("kernel32", SetLastError = true)]
    55. public static extern short GlobalAddAtom(string lpString);
    56. /// <summary>
    57. /// 热键的对应的消息ID
    58. /// </summary>
    59. public const int WM_HOTKEY = 0x312;
    60. [DllImport("kernel32", SetLastError = true)]
    61. public static extern short GlobalDeleteAtom(short nAtom);
    62. [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    63. public static extern int mouse_event(int dwflags, int dx, int dy, int cbuttons, int dwextrainfo);
    64. public const int mouseeventf_move = 0x0001; //移动鼠标
    65. public const int mouseeventf_leftdown = 0x0002; //模拟鼠标左键按下
    66. public const int mouseeventf_leftup = 0x0004; //模拟鼠标左键抬起
    67. public const int mouseeventf_rightdown = 0x0008; //模拟鼠标右键按下
    68. public const int mouseeventf_rightup = 0x0010; //模拟鼠标右键抬起
    69. public const int mouseeventf_middledown = 0x0020; //模拟鼠标中键按下
    70. public const int mouseeventf_middleup = 0x0040; //模拟鼠标中键抬起
    71. public const int mouseeventf_absolute = 0x8000; //标示是否采用绝对坐标
    72. }

    3、定时器

    1. timer = new System.Timers.Timer();
    2. timer.Enabled = true;
    3. timer.Interval = settingModel.intervals; //执行间隔时间,单位为毫秒
    4. timer.Elapsed += new System.Timers.ElapsedEventHandler(myTimedTask);

    4、快捷键

    1. int f1 = Win32Api.GlobalAddAtom("F1");
    2. int f5 = Win32Api.GlobalAddAtom("F5");
    3. int f6 = Win32Api.GlobalAddAtom("F6");
    4. Win32Api.RegisterHotKey(this.Handle, f1, Win32Api.KeyModifiers.None, (int)Keys.F1);
    5. Win32Api.RegisterHotKey(this.Handle, f5, Win32Api.KeyModifiers.None, (int)Keys.F5);
    6. Win32Api.RegisterHotKey(this.Handle, f6, Win32Api.KeyModifiers.None, (int)Keys.F6);
    1. /// <summary>
    2. /// 监视Windows消息
    3. /// </summary>
    4. /// <param name="m"></param>
    5. protected override void WndProc(ref Message m)
    6. {
    7. switch (m.Msg)
    8. {
    9. case Win32Api.WM_HOTKEY:
    10. ProcessHotkey(m);//按下热键时调用ProcessHotkey()函数
    11. break;
    12. }
    13. base.WndProc(ref m); //将系统消息传递自父类的WndProc
    14. }
    15. /// <summary>
    16. /// 按下设定的键时调用该函数
    17. /// </summary>
    18. /// <param name="m"></param>
    19. private void ProcessHotkey(Message m)
    20. {
    21. IntPtr id = m.WParam;//IntPtr用于表示指针或句柄的平台特定类型
    22. int sid = id.ToInt32();
    23. if (sid == f1)
    24. {
    25. //do something
    26. }
    27. else if (sid == f5)
    28. {
    29. //do something
    30. }
    31. else if (sid == f6)
    32. {
    33. //do something
    34. }
    35. }

    5、鼠标单击

    1. /// <summary>
    2. /// 单击
    3. /// </summary>
    4. /// <param name="x"></param>
    5. /// <param name="y"></param>
    6. public static void SingleClick(int x, int y)
    7. {
    8. //鼠标移动
    9. Win32Api.SetCursorPos(x, y);
    10. //鼠标点击
    11. Win32Api.mouse_event(Win32Api.mouseeventf_leftdown, x, y, 0, 0);
    12. Win32Api.mouse_event(Win32Api.mouseeventf_leftup, x, y, 0, 0);
    13. }

    五、不同模式代码简述

    1、开始监控

            监控开始的时候,首先检查是否正确设置了点击位置,然后开始定时器。

    1. switch (clickModes)
    2. {
    3. //间隔模式
    4. case ClickModes.ClickByStep:
    5. int x1, y1;
    6. if (!this.control0.ClickPositionFlag(out x1, out y1))
    7. {
    8. MessageBox.Show("请检查鼠标点击位置设置是否正确。");
    9. return;
    10. }
    11. break;
    12. //图片模式
    13. case ClickModes.ClickByPictures:
    14. if(this.control1.mat == null)
    15. {
    16. MessageBox.Show("请选择待匹配图片。");
    17. return;
    18. }
    19. break;
    20. }
    21. this.BackColor = Color.AliceBlue;
    22. this.startFlag = true;
    23. this.timer.Start();

    2、定时任务的代码 

    1. public void myTimedTask(object sender, System.Timers.ElapsedEventArgs e)
    2. {
    3. if (startFlag)
    4. {
    5. switch (this.clickModes)
    6. {
    7. //间隔模式
    8. case ClickModes.ClickByStep:
    9. int x0, y0;
    10. if (this.control0.ClickPositionFlag(out x0, out y0))
    11. {
    12. HelpTools.SingleClick(x0, y0);
    13. }
    14. break;
    15. //图片模式
    16. case ClickModes.ClickByPictures:
    17. int x2, y2;
    18. //检查是否再屏幕内匹配到用户上传的图片
    19. if(this.GetShouldClickFlagByPictures(out x2, out y2))
    20. {
    21. int x1, y1;
    22. //如果用户设置了点击位置,则点击设置的位置
    23. if(this.control1.ClickPositionFlag(out x1, out y1))
    24. {
    25. HelpTools.SingleClick(x1, y1);
    26. }
    27. //如果用户没有设置点击位置,则点击图片中心
    28. else
    29. {
    30. HelpTools.SingleClick(x2, y2);
    31. }
    32. }
    33. break;
    34. case ClickModes.ClickByText:
    35. break;
    36. case ClickModes.ClickByColor:
    37. break;
    38. case ClickModes.ClickByCustomize:
    39. break;
    40. default:
    41. break;
    42. }
    43. // 用于统计点击的次数
    44. runtimes++;
    45. // 如果是点击固定次数,则点击次数达到了的时候,停止运行
    46. if(settingModel!=null && settingModel.fixedFlag && runtimes>= settingModel.clickNums)
    47. {
    48. this.Invoke(new Action(() =>this.button5_Click(null, null)));
    49. }
    50. }
    51. }

    3、图片识别

            首先全屏截图,然后从全屏截图中使用用户上传的图片进行模板匹配,找到匹配的区域之后再将该区域抠出来和用户上传的图片进行一次结构相似性的比较(故此说这里未必次次都能正确找到,但是一般辨识度高的图片问题不太大)。

    1. /// <summary>
    2. /// 判断图片模式是否需要点击
    3. /// </summary>
    4. /// <returns></returns>
    5. public bool GetShouldClickFlagByPictures(out int x1, out int y1)
    6. {
    7. //全屏截图
    8. int width = Screen.PrimaryScreen.Bounds.Width;
    9. int height = Screen.PrimaryScreen.Bounds.Height;
    10. Rectangle smallRect = new Rectangle(0, 0, width, height);
    11. Rectangle tScreenRect = new Rectangle(0, 0, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
    12. Bitmap tSrcBmp = new Bitmap(width, height); // 用于屏幕原始图片保存
    13. Graphics gp = Graphics.FromImage(tSrcBmp);
    14. gp.CopyFromScreen(0, 0, 0, 0, smallRect.Size);
    15. gp.DrawImage(tSrcBmp, 0, 0, smallRect, GraphicsUnit.Pixel);
    16. //全屏截图转mat
    17. Mat mat = OpenCvSharp.Extensions.BitmapConverter.ToMat(tSrcBmp);
    18. Cv2.CvtColor(mat, mat, ColorConversionCodes.BGRA2BGR);
    19. //从全屏截图查找待匹配图像
    20. Mat out1 = new Mat();
    21. Cv2.MatchTemplate(mat, this.control1.mat, out1, TemplateMatchModes.CCoeffNormed);
    22. //寻找匹配到的最大最小值
    23. double minVal, maxVal;
    24. OpenCvSharp.Point minLoc, maxLoc;
    25. Cv2.MinMaxLoc(out1, out minVal, out maxVal, out minLoc, out maxLoc, null);
    26. //Cv2.Rectangle(mat, maxLoc, new OpenCvSharp.Point(maxLoc.X + this.control1.mat.Width, maxLoc.Y + this.control1.mat.Height), 255, 2);
    27. //获取匹配到的部分图像
    28. Mat child = new Mat(mat, new OpenCvSharp.Rect(maxLoc.X, maxLoc.Y, this.control1.mat.Width, this.control1.mat.Height));
    29. x1 = maxLoc.X + this.control1.mat.Width / 2;
    30. y1 = maxLoc.Y + this.control1.mat.Height / 2;
    31. //比较匹配到的和原图的相似度
    32. double ssim = ImagesTools.Compare_SSIM(child.Clone(), this.control1.mat.Clone());
    33. Console.WriteLine("相似性:" + ssim);
    34. if (ssim > 0.9)
    35. {
    36. return true;
    37. }
    38. System.GC.Collect();
    39. return false;
    40. }

  • 相关阅读:
    linux系统网络配置
    C# 使用正则表达式提取文字
    【定义】矩阵初等变换和矩阵等价
    SQL 性能优化总结
    重装系统以后怎么还原电脑原本的资料
    Python学习:元组教程
    STM32F1与STM32CubeIDE编程实例-红外寻迹传感器驱动
    Win10打开软件提示comdlg32.ocx文件丢失?
    使用Echarts.js绘制中国地图
    华为配置AP和AC之间NAT穿越示例
  • 原文地址:https://blog.csdn.net/bashendixie5/article/details/126441719