C# 系统应用之鼠标模拟技术及自动操作鼠标

游戏程序的操作不外乎两种——键盘输入控制和鼠标输入控制,几乎所有游戏中都使用鼠标来改变角色的位置和方向,本文主要是讲述如何使用C#调用Windows API函数实现鼠标模拟操作的功能.首先通过结合FindWindow和FindWindowEx寻找到窗体的按钮,在通过SetCursorPos或mouse_event函数操作鼠标,同时涉及到通过spy++工具获取窗体消息的信息.

一. Windows API函数介绍

.NET没有提供改变鼠标指针位置、模拟单机操作的函数,但是可以通过调用Windows API函数实现.

[DllImport("user32.dll")]

static extern bool SetCursorPos(int X,int Y);

该函数用于设置鼠标的位置,其中X和Y是相对于屏幕左上角的绝对位置.

[DllImport("user32.dll")]

static extern void mouse_event(MouseEventFlag flags,int dx,int dy,uint data,UIntPtr extraInfo);

该函数不仅可以设置鼠标指针绝对位置,而且可以以相对坐标来设置位置.

其中flags标志位集,指定点击按钮和鼠标动作的多种情况.dx指鼠标沿x轴绝对位置或上次鼠标事件位置产生以来移动的数量.dy指沿y轴的绝对位置或从上次鼠标事件以来移动的数量.data如果flags为MOUSE_WHEEL则该值指鼠标轮移动的数量(否则为0),正值向前转动.extraInfo指定与鼠标事件相关的附加32位值.

[DllImport("user32.dll")]

static extern IntPtr FindWindow(string strClass, string strWindow);

该函数根据类名和窗口名来得到窗口句柄,但是这个函数不能查找子窗口,也不区分大小写.如果要从一个窗口的子窗口查找需要使用FIndWindowEX函数.

[DllImport("user32.dll")]

static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,

string strClass, string strWindow);

该函数获取一个窗口的句柄,该窗口的类名和窗口名与给定的字符串相匹配,该函数查找子窗口时从排在给定的子窗口后面的下一个子窗口开始.其中参数

hwnParent为要查找子窗口的父窗口句柄,若该值为NULL则函数以桌面窗口为父窗口,查找桌面窗口的所有子窗口.

hwndChildAfter子窗口句柄,查找从在Z序中的下一个子窗口开始,子窗口必须为hwnParent直接子窗口而非后代窗口,若hwnChildAfter为NULL,查找从父窗口的第一个子窗口开始.

strClass指向一个指定类名的空结束字符串或一个标识类名字符串的成员的指针.

strWindow指向一个指定窗口名(窗口标题)的空结束字符串.若为NULL则所有窗体全匹配.

返回值:如果函数成功,返回值为具有指定类名和窗口名的窗口句柄,如果函数失败,返回值为NULL.

二. 鼠标自动点击按钮和查看鼠标运行轨迹

首先创建一个C#工程,设计的窗体如下图所示,同时添加Timer时间器控件:

然后添加的如下代码,即可实现鼠标模拟技术及自动操作鼠标:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
//引用新命名空间
using System.Runtime.InteropServices;  //StructLayout

namespace MouseAction
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //结构体布局 本机位置
        [StructLayout(LayoutKind.Sequential)]
        struct NativeRECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }

        //将枚举作为位域处理
        [Flags]
        enum MouseEventFlag : uint //设置鼠标动作的键值
        {
            Move = 0x0001,               //发生移动
            LeftDown = 0x0002,           //鼠标按下左键
            LeftUp = 0x0004,             //鼠标松开左键
            RightDown = 0x0008,          //鼠标按下右键
            RightUp = 0x0010,            //鼠标松开右键
            MiddleDown = 0x0020,         //鼠标按下中键
            MiddleUp = 0x0040,           //鼠标松开中键
            XDown = 0x0080,
            XUp = 0x0100,
            Wheel = 0x0800,              //鼠标轮被移动
            VirtualDesk = 0x4000,        //虚拟桌面
            Absolute = 0x8000
        }

        //设置鼠标位置
        [DllImport("user32.dll")]
        static extern bool SetCursorPos(int X, int Y);

        //设置鼠标按键和动作
        [DllImport("user32.dll")]
        static extern void mouse_event(MouseEventFlag flags, int dx, int dy,
            uint data, UIntPtr extraInfo); //UIntPtr指针多句柄类型

        [DllImport("user32.dll")]
        static extern IntPtr FindWindow(string strClass, string strWindow);

        //该函数获取一个窗口句柄,该窗口雷鸣和窗口名与给定字符串匹配 hwnParent=Null从桌面窗口查找
        [DllImport("user32.dll")]
        static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,
            string strClass, string strWindow);

        [DllImport("user32.dll")]
        static extern bool GetWindowRect(HandleRef hwnd, out NativeRECT rect);

        //定义变量
        const int AnimationCount = 80;
        private Point endPosition;
        private int count;

        private void button1_Click(object sender, EventArgs e)
        {
            NativeRECT rect;
            //获取主窗体句柄
            IntPtr ptrTaskbar = FindWindow("WindowsForms10.Window.8.app.0.2bf8098_r11_ad1", null);
            if (ptrTaskbar == IntPtr.Zero)
            {
                MessageBox.Show("No windows found!");
                return;
            }
            //获取窗体中"button1"按钮
            IntPtr ptrStartBtn = FindWindowEx(ptrTaskbar, IntPtr.Zero, null, "button1");
            if (ptrStartBtn == IntPtr.Zero)
            {
                MessageBox.Show("No button found!");
                return;
            }
            //获取窗体大小
            GetWindowRect(new HandleRef(this, ptrStartBtn), out rect);
            endPosition.X = (rect.left + rect.right) / 2;
            endPosition.Y = (rect.top + rect.bottom) / 2;
            //判断点击按钮
            if (checkBox1.Checked)
            {
                //选择"查看鼠标运行的轨迹"
                this.count = AnimationCount;
                movementTimer.Start();
            }
            else
            {
                SetCursorPos(endPosition.X, endPosition.Y);
                mouse_event(MouseEventFlag.LeftDown, 0, 0, 0, UIntPtr.Zero);
                mouse_event(MouseEventFlag.LeftUp, 0, 0, 0, UIntPtr.Zero);
                textBox1.Text = String.Format("{0},{1}", MousePosition.X, MousePosition.Y);
            }

        }

        //Tick:定时器,每当经过多少时间发生函数
        private void movementTimer_Tick(object sender, EventArgs e)
        {
            int stepx = (endPosition.X - MousePosition.X) / count;
            int stepy = (endPosition.Y - MousePosition.Y) / count;
            count--;
            if (count == 0)
            {
                movementTimer.Stop();
                mouse_event(MouseEventFlag.LeftDown, 0, 0, 0, UIntPtr.Zero);
                mouse_event(MouseEventFlag.LeftUp, 0, 0, 0, UIntPtr.Zero);
            }
            textBox1.Text = String.Format("{0},{1}", MousePosition.X, MousePosition.Y);
            mouse_event(MouseEventFlag.Move, stepx, stepy, 0, UIntPtr.Zero);
        }
    }
}

同时自定义一个对话框,增加一个button按钮,其运行结果如下图所示:

可以看到当运行程序勾选"查看鼠标运行的轨迹"并点击"开始"按钮后,会通过FindWindow和FindWindowEx函数查找窗体"Form1"的"button1"按钮,并通过mouse_event移动鼠标和点击鼠标.其中函数原型为:

IntPtr FindWindowEx(
    IntPtr hwndParent,     // handle to parent window [父窗体句柄]
    IntPtr hwndChildAfter, // handle to child window [子窗体句柄]
    string strClass,       // class name [窗体类名]
    string strWindow       // window name [窗体名]
);

但是怎样找到窗体类名和按钮的类名呢?由于初学,很多窗体我都没有实现如QQ,它需要用到一个叫spy++的工具.

PS:第一次制作gif格式动态图片,参照博客 http://blog.csdn.net/tangcheng_ok/article/details/8246792

三. 使用SPY++工具获取窗体信息

如果修改代码为:

//获取任务栏句柄
IntPtr ptrTaskbar = FindWindow("Shell_TrayWnd",null);
//托盘通知句柄
IntPtr ptrStartBtn = FindWindowEx(ptrTaskbar, IntPtr.Zero, "TrayNotifyWnd", null);  

可以获取电脑底部任务栏的托盘通知句柄,其中通过Spy++工具(VS中"工具"中自带)查找如下图所示:

同样,我通过spy++工具获取txt句柄,首先打开spy++工具,同时点击"查找窗口"按钮(望远镜),再点击"查找程序工具"中按钮拖拽至要查看的窗体中,点击"确定"按钮.

这样就会显示这个txt的信息,同时可以右击"属性"显示窗体的类名、窗体题目、句柄等信息.

最后通过下面代码可以获取hello.txt的句柄:

//获取记事本句柄
IntPtr ptrTaskbar = FindWindow("Notepad", null);
IntPtr ptrStartBtn = FindWindowEx(ptrTaskbar, IntPtr.Zero, "Edit", null);

再通过mouse_event操作鼠标,同时可以通过SendMessage将指定的消息发送到一个或多个窗口,PostMessage将一个消息寄送到一个线程的消息队列后就立即返回.实现消息传递等功能,学习ing~

四. 总结

该篇文章主要讲述C#如何操作鼠标的事件,在制作游戏外挂或自动运行程序时非常实用,但遗憾的是在上面通过窗体名称"Form1"获取窗体时总是失败,需要通过spy++获取它的类名来实现.Why?同时如果想学习键盘模拟技术的可以研究SetWindowsHookEx(安装钩子)、CallNextHookEx(下一个钩子)、UnhookWindowsHookEx(卸载钩子)和鼠标Hook实现很多技术.

希望文章对大家有所帮助,如果有错误或不足之处,请见谅~

(By:Eastmount 2014年10月13日 晚上8点
http://blog.csdn.net/eastmount/)

参考资料-在线笔记:

本文主要参考书籍《C#网络变成高级篇之网页游戏辅助程序设计》张慧斌 王小峰著

1.C#获取QQ聊天输入框中内容 http://www.csharpwin.com/csharpspace/9133r5654.shtml

2.C#查找窗口,FindWindow用法(By-LYBwwp)http://blog.csdn.net/lybwwp/article/details/8168553

3.FindWindowEx用法(By-coolszy)
http://blog.csdn.net/coolszy/article/details/5523784

4.C# 隐藏任务栏开始按钮关闭shell(By-sshhbb)http://blog.csdn.net/sshhbb/article/details/6605976

5.任务栏句柄 http://blog.csdn.net/wangjieest/article/details/6943241

6.C#如何在外部程序的密码框内自动输入密码
http://biancheng.dnbcw.info/c/117849.html

7.C#实现对外部程序的调用操作 http://www.blue1000.com/bkhtml/c17/2012-11/70993.htm

8.百度知道 C#
API函数FindWindowEx返回子窗体的值为零

9.百度知道 用C#操作API实现填写桌面窗体内的textbox并点击窗体按钮

时间: 2024-10-04 22:06:20

C# 系统应用之鼠标模拟技术及自动操作鼠标的相关文章

[转]C# 系统应用之鼠标模拟技术及自动操作鼠标

原文网址: C# 系统应用之鼠标模拟技术及自动操作鼠标        游戏程序的操作不外乎两种——键盘输入控制和鼠标输入控制,几乎所有游戏中都使用鼠标来改变角色的位置和方向,本文主要是讲述如何使用C#调用Windows API函数实现鼠标模拟操作的功能.首先通过结合FindWindow和FindWindowEx寻找到窗体的按钮,在通过SetCursorPos或mouse_event函数操作鼠标,同时涉及到通过spy++工具获取窗体消息的信息. 一. Windows API函数介绍 .NET没有提

C#之鼠标模拟技术

游戏程序的操作不外乎两种——键盘输入控制和鼠标输入控制,几乎所有游戏中都使用鼠标来改变角色的位置和方向,本文主要是讲述如何使用C#调用Windows API函数实现鼠标模拟操作的功能.首先通过结合FindWindow和FindWindowEx寻找到窗体的按钮,在通过SetCursorPos或mouse_event函数操作鼠标,同时涉及到通过spy++工具获取窗体消息的信息. 鼠标自动点击按钮和查看鼠标运行轨迹: 首先创建一个C#工程,设计的窗体如下图所示,同时添加Timer时间器控件: 然后添加

C#/winform 自动触发鼠标、键盘事件

要在C#程序中触发鼠标.键盘事件必须要调用windows函数. 一.鼠标事件的触发 1.引用windows函数mouse_event /// <summary> /// 鼠标事件 /// </summary> /// <param name="flags">事件类型</param> /// <param name="dx">x坐标值(0~65535)</param> /// <param

WPF窗口长时间无人操作鼠标自动隐藏

原文:WPF窗口长时间无人操作鼠标自动隐藏 在软件开发中有时会有等待一段时间无人操作后隐藏鼠标,可能原因大致如下: 1.为了安全性,特别是那些需要用到用户名和密码登录服务端的程序,常常考虑长期无人操作,程序自动跳转到用户登录界面: 2.软件为了更好的播放效果,需要隐藏鼠标. 这里写的是第二种情况,wpf做播放时,需要隐藏鼠标. 思路是:假如3s鼠标不动则隐藏,设计了计时器的间隔时间为1s,并添加鼠标没移动的计数器,计数器达到3才执行程序.实现是这样的:每隔1s检测鼠标是否移动,如果不移动则计数器

使用C#: 自动切换鼠标的左右手习惯

不知道我得的是鼠标手,还是肩周炎. 长时间右手(或者左手)使用鼠标的话,那只胳膊便会不自在. 于是便有了切换鼠标主次要键的需求. [控制面板->鼠标]有更改它的设置,可点来点去让我觉得不够方便. 我需要的是“一个命令就能搞定它”,这样我就可以在命令行,或者程序加载器里面方便的运行他. 下面的代码便是要实现这一需求: 他是一个命令行程序.如果当前鼠标是右手习惯,则将鼠标习惯设置为左手,反之设置成右手习惯. 实现代码如下: C#代码   using System; using System.Runt

关闭myeclipse中烦人的鼠标划过,自动提示功能

eclipse越来越智能,身为码农的我却越来越伤心.虽然你很智能,但请你提供一些有用的信息给我,不要乱七八槽的,不问青红皂白就塞一大堆提示给我,对不起,哥不需要这些!!! 都知道,使用myeclipse开发java,jsp,js时,鼠标划过某个类,对象,方法……会自动提示一些没有用的信息(至少对我来说),好烦人啊,终于到了忍无可忍的时候,决定将这没用的提示干掉,还我一个清静世界. 我的是myeclipse10.1,请先打开window->preferences 执行一下3步 1.java->e

[UE4]工程设置:自动捕获鼠标、通过代码设置鼠标显示隐藏、输入模式、编译时自动保存

一.在4.20版本中运行游戏,在没有进行任何设置的情况下,游戏不会自动捕获鼠标,游戏不会接受输入,需要手动点一下游戏界面才行.如果要跟老版本一样运行游戏自动捕获鼠标,需要进行设置 二.也可以通过代码的方式设置鼠标显示隐藏和输入模式 三.编译时自动保存 原文地址:https://www.cnblogs.com/timy/p/10189754.html

绕过超星学习通鼠标移开自动暂停视频的方法

绕过超星学习通鼠标移开自动暂停视频的方法 说明 原理:通过开发者模式删除鼠标监听事件 安全性:本方法不会被学习通检测以及警告,可放心使用 其他:可以用油猴插件进行自动挂机学习,但被学习通警告的风险较大(该门成绩分数为0) 1. 进入页面 用Google Chrome浏览器打开超星学习通页面,并进入到学生学习页面(其他浏览器大同小异) 2. 进入开发者模式 按下快捷键F12进入开发者模式 3. 打开监听事件 选择Event Listeners事件监听选项卡 4. 选择鼠标监听事件 点击mouseo

利用python模拟鼠标点击自动完成工作,提升你的工作效率!

没有什么能比学以致用让学习变得更有动力的了. 不知道大家在工作中有没有一些工作需要重复的点击鼠标,因为会影响到财务统计报表的关系,我们每个月底月初都要修改ERP中的单据日期,单据多的时候光修改就能让你点鼠标点到手麻.(这里要吐槽一下浪沙软件,别的单据都可以批量修改日期,就是这个移仓单不行,你们研发怎么就这么懒?剩下这么点工作就不完成他?)之前同事有跟我提到过键盘精灵,因为当时负责别的工作,一直没有去了解.现在公司人员减少了,这工作又回到我身上了,刚好之前我在学习连连看的时候,知道Python 也