一.写在前面

作为一个人力资源工作者,会经常遇到填表、报表的事务,其实有时候就是重复再重复的点击鼠标工作,特别是遇到一些复杂的客户端程序、网页程序,诸如用友客户端、社保管理系统等等,就尤其让人头疼。正好这段时间做了很多这方面的工作,搜索了不少的资料,为了转化学习效果,记录于此,温故知新。

二.引用Windows API

c#模拟鼠标操作,就必须和WindowsAPI打交道,通过引用它内部的几个函数,从而实现在屏幕的指定位置单击、双击,或者对指定的窗体(能够获得句柄的)、控件进行相关控制操作。相关函数如下:

SetCursorPos(设置鼠标位置) mouse_event(控制鼠标动作)
FindWindow(获得窗口的句柄) FindWindowEx(获得子窗口或控件的句柄)

SetCursorPos设置鼠标位置
//设置鼠标位置
[DllImport("user32.dll")] //DllImpor针对非托管的。非托管指的是不利用.net 生成的DLL
//声明一个外部实现方法SetCursorPos()
public static extern bool SetCursorPos(int X, int Y);

这里定义声明动态链接库user32.dll作为静态入口点。SetCursorPos是这个动态链接库里面的内部方法,所以这里不要试图改变大小写什么的。这里定义的方法使用 extern 修饰符意味着该方法在 C# 代码外部实现。extern 修饰符的常见用法是在使用 Interop 服务调入非托管代码时与DllImport 特性一起使用。在这种情况下,还必须将方法声明为static

mouse_event控制鼠标动作
//控制鼠标动作
[DllImport("user32.dll")]
public static extern void mouse_event(MouseEventFlag flags, int dx, int dy, uint data, UIntPtr extraInfo);

MouseEventFlag 继承uint(uint型为无符号32位整数,占4个字节,取值范围在0~4,294,967,295之间。)的枚举,指代一组鼠标动作标志位集;
dx指鼠标沿x轴绝对位置或上次鼠标事件位置产生以来移动的像素数量;dy指沿y轴的绝对位置或从上次鼠标事件以来移动的像素数量;
data变量是指,如果flags为MOUSE_WHEEL,则该变量指定鼠标轮移动的数量。正值表明鼠标轮向前转动,即远离用户的方向;负值表明鼠标轮向后转动,即朝向用户。一个轮击定义为WHEEL_DELTA,即120。如果flags不是MOUSE_WHEEL,则data应为零;
extraInfo指定与鼠标事件相关的附加32位值,应用程序调用函数GetMessageExtraInfo来获得此附加信息。一般的情况下赋值IntPtr.ZeroIntPtr用于表示指针或句柄的特定类型(A platform-specific type that is used to represent a pointer or a handle.)它被设计成整数,其大小适用于特定平台。它们主要用于本机资源,如窗口句柄)

FindWindow获得窗口的句柄
//在窗口列表中寻找与指定条件相符的第一个窗口,并返回句柄值。这个函数不能查找子窗口。
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

lpClassName是窗口的类名, lpWindowName是窗口的标题。这两个变量可以用Spy++软件来获得。
在搜索的时候不一定两者都知道,但至少要知道其中的一个(不知道的可以赋值null)。有的窗口的标题是比较容易得到的,如"计算器",所以搜索时应使用标题进行搜索。但有的软件的标题不是固定的,如"记事本",如果打开的文件不同,窗口标题也不同,这时使用窗口类搜索就比较方便。如果找到了满足条件的窗口,这个函数返回该窗口的句柄,否则返回0。如果查找子窗口需要用FindWindowEx

FindWindowEx获得窗口或者控件的句柄
//该函数获得一个窗口的句柄,该窗口的类名和窗口名与给定的字符串相匹配。这个函数查找子窗口,从排在给定的子窗口后面的下一个子窗口开始。在查找时不区分大小写。
[DllImport("user32.dll", EntryPoint = "FindWindowEx", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

hwndParent,父窗口句柄,如果hwndParent为 0 ,则函数以桌面窗口为父窗口,查找桌面窗口的所有子窗口。
hwndChildAfter,子窗口句柄,查找从在Z序中的下一个子窗口开始。子窗口必须为hwndParent窗口的直接子窗口而非后代窗口。如果HwndChildAfter为NULL,查找从hwndParent的第一个子窗口开始。如果hwndParent 和 hwndChildAfter同时为NULL,则函数查找所有的顶层窗口及消息窗口。
lpszClass ,窗口或控件类名;lpszWindow ,窗口或控件标题。如果该参数为 NULL,则为所有窗口全匹配。

三.应用

1 首先新建一个类-类名WindowApi

using System;
using System.Runtime.InteropServices;//需要引用,从而使相应的类或者方法来支持托管/非托管模块间的互相调用namespace 模拟输入
{public static class WindowApi{#region 鼠标操作//首先定义一个枚举,其继承uint。这样可以直观的体现鼠标的各类动作。//[Flags]位标志属性,从而使该枚举类型的实例可以存储枚举列表中定义值的任意组合。可以用 与(&)、或(|)、异或(^)进行相应的运算。[Flags]public 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")]public static extern bool SetCursorPos(int X, int Y);//设置鼠标按键和动作[DllImport("user32.dll")]public static extern void mouse_event(MouseEventFlag flags, int dx, int dy, uint data, UIntPtr extraInfo);//方法:鼠标左键单击操作:鼠标左键按下和松开两个事件的组合即一次单击public static void MouseLeftClickEvent(int dx, int dy, uint data){SetCursorPos(dx, dy);System.Threading.Thread.Sleep(2 * 1000);mouse_event(MouseEventFlag.LeftDown|MouseEventFlag.LeftUp, dx, dy, data, UIntPtr.Zero);}//方法:鼠标右键单击操作:鼠标右键键按下和松开两个事件的组合即一次单击public static void MouseRightClickEvent(int dx, int dy, uint data){SetCursorPos(dx, dy);System.Threading.Thread.Sleep(2 * 1000);mouse_event(MouseEventFlag.RightDown|MouseEventFlag.RigthtUp, dx, dy, data, UIntPtr.Zero);}#endregion#region 句柄函数//获得窗口的句柄[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);//获得子窗口、子控件的句柄;需要提前知道父窗体的句柄,以及窗口的类名或者标题名。[DllImport("user32.dll", EntryPoint = "FindWindowEx", SetLastError = true)]public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);//该函数返回指定窗口的边框矩形的尺寸。该尺寸以相对于屏幕坐标左上角的屏幕坐标给出[DllImport("user32.dll")]public static extern bool GetWindowRect(IntPtr hwnd, out NativeRECT rect);#endregion  }
}

2 在WinForm中使用刚才新建的WindowApi

假如:有这么一个程序,我们要进行这样的操作“ 点击鼠标到查询文本框,输入查询关键字,点击查询按钮,获得查询的内容,然后在点击窗体上的打印按钮,调出win系统的打印对话框(输出PDF),输入PDF的文件名,最后打印输出。这样的动作循环若干次。 ”因为无法获得这个程序里面查询文本框控件的句柄,我们就必须模拟鼠标的操作,点中这个查询框,然后用SendKeys.SendWait("待查询关键字"),发送给定的内容,然后再用鼠标点击这个查询按钮。

    public partial class Form1 : Form{public Form1(){InitializeComponent();}private void BtnOutPut_Click(object sender, EventArgs e){string str="查询关键字";Thread.Sleep(1000);for (int i = 0; i < 10; i++){str += i;Clipboard.SetText(str);WindowApi.MouseLeftClickEvent(800, 160, 0);//将鼠标定位到文本框,假设文本框的中间位置的坐标是800,160//由于SendKeys.SendWait不能发送中文字符,所以只能用复制粘贴的方式折中。SendKeys.SendWait("^A");//全选SendKeys.SendWait("^V");//将剪贴板的内容粘贴。Thread.Sleep(1000);WindowApi.MouseLeftClickEvent(950, 160, 0);//将鼠标点击查询按钮WindowApi.MouseLeftClickEvent(1000, 300, 0);//将鼠标点击打印按钮//根据句柄找到打印窗体IntPtr ptrTaskbar = WindowApi.FindWindow(null, "打印设置");if (ptrTaskbar != IntPtr.Zero){IntPtr ptrOKBtn = WindowApi.FindWindowEx(ptrTaskbar, IntPtr.Zero, "TButton", "确认");//找到打印窗体中的确定按钮并发送确认信息。WindowApi.GetWindowRect(ptrOKBtn, out WindowApi.NativeRECT rect);this.textControlPos.Text = rect.bottom.ToString() + "--" + rect.left.ToString();WindowApi.SetCursorPos(rect.left + 40, rect.bottom - 15);//将鼠标定位到打印按钮//Thread.Sleep(1000);//delay 5mWindowApi.mouse_event(WindowApi.MouseEventFlag.LeftDown, 0, 0, 0, UIntPtr.Zero);//单击鼠标左键WindowApi.mouse_event(WindowApi.MouseEventFlag.LeftUp, 0, 0, 0, UIntPtr.Zero);//单击鼠标左键Thread.Sleep(5000);//定位打印输出窗体IntPtr ptrPrintOutputForm = WindowApi.FindWindow(null, "将打印输出另存为");IntPtr ptrPrintOutputForm_Edit = WindowApi.FindWindowEx(ptrPrintOutputForm, "Edit", true);//。this.textControlPos.Text = ptrPrintOutputForm_Edit.ToString() + "----" + ptrPrintOutputForm.ToString();WindowApi.SendMessage(ptrPrintOutputForm_Edit, 0x000C, null,str);//输入另存为的文件名IntPtr ptrPrintOutputForm_Save = WindowApi.FindWindowEx(ptrPrintOutputForm, IntPtr.Zero, "Button", "保存(&S)");WindowApi.SendMessage(ptrPrintOutputForm_Save, 0xF5, 0, 0);//保存}else{MessageBox.Show("未能找到打印窗体");}str = "查询关键字";}}

参考资料

C# 模拟鼠标移动与点击
C# 系统应用之鼠标模拟技术及自动操作鼠标
C#应用WindowsApi实现查找\枚举(FindWindow、EnumChildWindows)窗体控件,并发送消息。
c#里FindWindow的用法
C#模拟鼠标和键盘操作

【向重复工作说不】c#之模拟鼠标操作相关推荐

  1. 利用Python模拟鼠标操作,一键将GIF文件载入CSDN的Markdown编辑器

    简 介: 讨论了在csdn的Markdown编译器中自动载入GIF文件的方法.使用了Python中的pyautogui软件包,可以通过Python的小程序实现一键将GIF文件载入CSDN的Markdo ...

  2. C# SendInput 实现模拟鼠标操作

    刚刚开始学习C#,想自己做一个网页游戏的挂.游戏里面有收钱的动作,一个建筑物一个建筑物的点,很累啊.于是想用C#模拟鼠标操作替我收钱,想着学习这个对以后的测试工作也有帮助,于是有了动力.学习过程也是曲 ...

  3. 使用python pynput完全模拟鼠标操作

    人生苦短,我用Python! 总是有些事情需要重复性机械性的操作,想要设计代码去做,有些程序又不给接口,这时最简单的方法就是,模拟鼠标操作! 除了不能生孩子,其他都能做的python!  一搜 果然有 ...

  4. 使用pymouse库模拟鼠标操作,做一些简单游戏辅助

    最近在玩爱江山更爱美人,里面不少地方可以使用pymouse库模拟鼠标操作 帮助做一些重复简单的任务 pymouse库的安装 使用方法,可以看一下代码 一.钓鱼 #将窗口放在右上角 #x,y 为开始im ...

  5. C#模拟鼠标操作以及键盘输入

    模拟鼠标以及键盘基本输入源代码 using System; using System.Collections.Generic; using System.Linq; using System.Runt ...

  6. 真就释放双手?C#模拟鼠标操作

    前言 最近玩贪玩蓝月,老是让我呆呆站着领在线礼包?我可能每过几分钟就领一下礼包吗?害,要是有人能帮我一会点一下鼠标就好了- -于是,奇怪的知识又增加了- - 页面设计 注:因为控件可能有点多,代码中也 ...

  7. pywinauto客户端自动化---模拟鼠标操作

    无论什么自动化,都避免不了模拟一些鼠标操作,pywinauto模块中也存在模拟鼠标操作方法 mouse mouse是pywinauto一个模拟鼠标的方法库,可以帮助模拟鼠标操作,直接输入坐标就可以了. ...

  8. Mouse.bat 模拟鼠标操作脚本

    mouse.bat 模拟鼠标操作, 调用方式 //clicks at the current position call mouse click //double clicks at the curr ...

  9. 鼠标在linux下如何工作,Linux操作系统下的鼠标操作

    本不想写这些玩意儿,其实我并不喜欢它们,只不过没办法,谁叫我是靠这些家伙糊口的呢! 若干年前,我自认为很喜欢Linux操作系统:若干年后,我发现我当初是何等的天真. 恩,一分钱,一分货!我坚信这一点! ...

最新文章

  1. 几个cvebase_ifo基础信息融合在一起
  2. iOS 可能用到的三方框架
  3. Django搭建简易博客教程(四)-Models
  4. java xpath 命名空间_【转】玩转 XPath 和缺省命名空间(Default Namespaces)
  5. 英特尔开源WebRTC开发套件OWT
  6. why my own list cannot automatically scroll to load oData
  7. python中input()与raw_input()的区别到底是啥?-----marsggbo原创作品为你解答
  8. vue_cli全局变量使用
  9. 研究机构:全球半导体厂商今年资本支出1081亿美元
  10. MySQL 成薪资跳板了?
  11. gradle生成java文件_使用Gradle for Java插件生成Java类
  12. Scrapy创建zentao爬虫
  13. Android 完整地操作数据库--日记本实例
  14. ttf能改成gfont吗_如何修改TTF字体
  15. 模拟电路实现延时功能
  16. 可以下载视频的手机浏览器
  17. 满天星java代码6_java实现的满天星效果的方法
  18. Ubuntu系统---安装搜狗输入法
  19. 计算机b级及格线,计算机一级B考试有那些内容?理论部分不几个就算总分及格了也不算合格吗?...
  20. (5.2.3)配置服务器参数——服务器性能估算

热门文章

  1. 阿里云OSS对象存储服务的使用
  2. SSH连接工具FinalShell的安装与使用
  3. 微软亚太研发集团高性能计算首席架构师徐明强访谈:我的成长启示录
  4. matlab 里的数据粘贴不出来怎么办,为什么excel表格数据复制不出来怎么办_为什么excel中内容不能复制粘贴...
  5. esp8266 airkiss联网进行TCP,UDP通信服务
  6. C语言实现10只小猪称体重
  7. 微信小程序自定义输入框个数
  8. Chrome 翻译功能
  9. [资源下载]张宇老师讲授的数学视频(概率+高数+线代)
  10. itertools:Python3迭代库(持续更新ing...)