在WebBrowser中通过模拟键盘鼠标操控网页中的文件上传控件

引言

image 这两天沉迷了Google SketchUp,刚刚玩够,一时兴起,研究了一下WebBrowser。

我在《WebBrowser控件使用技巧分享》一文中曾谈到过“我现在可以通过WebBrowser实现对各种Html元素的操控,唯独无法控制Html的上传控件”,出于安全原因,IE没有对上传控件提供操控支持,这使得我们没法像控制其他控件一样用简单的代码进行赋值。

比较实际的解决方案就是模拟操作了,下面我就将演示通过键盘、鼠标两种方式模拟点击“浏览”按钮,然后配合键盘模拟输入文件路径,并按回车键确认。

初始环境

测试使用了一个简单的HTML页面,页中各个位置中分布了一些文件上传控件,有些是直接放置的,有些是横向排列的,有些是嵌套在表格中的,用以测试不同位置的触发效果:

image 

将此页面用WebBrowser控件加载。

在程序界面中,我放置了一些控件用于选择上传文件所在目录,测试时首先选定一个包含有文件的目录,然后从中随机抽选一个文件填写到上传控件中:

image

并在代码中建立了一个辅助方法,用以读取页面上所有的文件上传控件,在测试时也是从中随机抽取一个进行操控:

List<HtmlElement> 读取上传控件()

{

    var l = new List<HtmlElement>();

    foreach (HtmlElement f in webBrowser1.Document.GetElementsByTagName("input"))

    {

        if (f.GetAttribute("type") == "file")

        {

            l.Add(f);

        }

    }

    return l;

}

在类中定义了一个Random类型成员变量用于生成随机数:

Random R = new Random();

此外还定义了一系列方法,用于在点击按钮后,延迟3秒以等待文件浏览对话框打开,然后模拟输入文件路径,再模拟输入回车键确定:

void 延迟操作对话框(string 填写文件路径)

{

    button1.Enabled = button2.Enabled = button3.Enabled = false;

    BackgroundWorker b = new BackgroundWorker();

    b.RunWorkerCompleted += new RunWorkerCompletedEventHandler(b_RunWorkerCompleted);

    b.DoWork += new DoWorkEventHandler(b_DoWork);

    b.RunWorkerAsync(填写文件路径);

}

 

void b_DoWork(object sender, DoWorkEventArgs e)

{

    Thread.Sleep(3000);

    e.Result = e.Argument;

}

 

void b_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

{

    SendKeys.Send(e.Result as string);

    SendKeys.Send("{Enter}");

    button1.Enabled = button2.Enabled = button3.Enabled = true;

}

这里就是通过使用BackgroundWorker组件在后台延迟3秒,延迟结束后在回调事件中进行操作。

键盘模拟方式

先看录制的动画:

2009-8-5 23-01-36

在这里首先选择了一个上传文件所在目录,然后进行了几次键盘模拟操作测试。

键盘模拟的操作流程如下:

首先激活WebBrowser控件

然后让文件上传控件获取焦点,这时光标会处于文件上传控件左侧的文本框内

模拟输入Tab键切换焦点到“浏览..”按钮

模拟输入空格键点击该按钮

然后就是延迟3秒等待文件选取对话框显示,模拟输入文件路径并模拟输入回车键即可

主要代码如下:

private void button1_Click(object sender, EventArgs e)

{

    var l = 读取上传控件();

    var s = Directory.GetFiles(folderBrowserDialog1.SelectedPath);

    键盘操作(l[R.Next(l.Count)], s[R.Next(s.Length)]);

}

 

void 键盘操作(HtmlElement 元素, string 填写文件路径)

{

    webBrowser1.Select();

    webBrowser1.Focus();

    元素.Focus();

    SendKeys.Send("{Tab}");

    SendKeys.Send(" ");

    延迟操作对话框(填写文件路径);

}

鼠标模拟方式

还是先看录制的动画:

2009-8-5 23-16-51

鼠标模拟的主要流程是:

首先递归计算页面中的文件上传控件相对于页面左上角的坐标位置

接着再递归计算WebBrowser控件相对于屏幕左上角的位置

在将位置值加上控件自身宽度及高度,并辅以修正值,以确保鼠标能够点到按钮上面

移动鼠标到计算好的位置处

单击鼠标

然后也是延迟3秒等待文件选取对话框显示,模拟输入文件路径并模拟输入回车键

递归计算页面元素相对于页面坐上角位置的函数:

Point 计算坐标(HtmlElement 元素, Point 起始坐标)

{

    var p = 起始坐标;

    p.Offset(元素.OffsetRectangle.Location);

    return 元素.OffsetParent == null ? p : 计算坐标(元素.OffsetParent, p);

}

递归计算控件相对于屏幕左上角位置的函数:

private Point 计算坐标(Control 控件, Point 起始坐标)

{

    var p = 起始坐标;

    p.Offset(控件.Location);

    return 控件.Parent == null ? p : 计算坐标(控件.Parent, p);

}

此外,为了模拟鼠标移动和点击,还需要引入Windows API:

[DllImport("User32")]

public extern static void SetCursorPos(int x, int y);

 

[DllImport("user32.dll")]

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

 

[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

}

主要代码如下:

private void button2_Click(object sender, EventArgs e)

{

    var l = 读取上传控件();

    var s = Directory.GetFiles(folderBrowserDialog1.SelectedPath);

    鼠标操作(l[R.Next(l.Count)], s[R.Next(s.Length)]);

}

 

void 鼠标操作(HtmlElement 元素, string 填写文件路径)

{

    var p = 计算坐标(元素, new Point());

    p = 计算坐标(webBrowser1, p);

    p.Offset(元素.OffsetRectangle.Width - 5, 元素.OffsetRectangle.Height + 15);

    SetCursorPos(p.X, p.Y);

    mouse_event(MouseEventFlag.LeftDown | MouseEventFlag.LeftUp, 0, 0, 0, 0);

    延迟操作对话框(填写文件路径);

}

总结

两种方法中推荐使用键盘模拟方法,简单而直接;鼠标模拟方法需要使用到API,还需要精确计算,并且如果页面带有滚动条,且滚动条进行了滚动或文件上传控件处于可视区之外的话,将无法通过上述方法计算和操控,而其优点仅仅是直观一些而已。

 

下载本文的示例源代码:http://www.uushare.com/user/icesee/file/1869210

下载本文的XPS版本:http://www.uushare.com/user/icesee/file/1869213

转载于:https://www.cnblogs.com/SkyD/archive/2009/08/06/1540021.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/379582.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

编写最简单的字符设备驱动

编写最简单的字符设备驱动1 编写驱动代码2 编写makefile3 编译和加载驱动4 编写应用程序测试驱动参考文章&#xff1a; linux驱动开发第1讲&#xff1a;带你编写一个最简单的字符设备驱动 linux驱动开发第2讲&#xff1a;应用层的write如何调用到驱动中的write 1 编写驱动代码…

Java ObjectStreamField toString()方法与示例

ObjectStreamField类toString()方法 (ObjectStreamField Class toString() method) toString() method is available in java.io package. toString()方法在java.io包中可用。 toString() method is used to return a string that defines this field. toString()方法用于返回定…

linux内核文件描述符fd、文件索引节点inode、文件对象file关系

文件描述符fd、文件索引节点inode、文件对象file关系1 VFS对象1.1 超级块对象1.2 索引节点对象1.3 文件对象1.4 进程描述符1.5 files_struct2 如何根据文件描述符fd找到文件&#xff1f;1 VFS对象 在说fd、inode和file关系之前&#xff0c;我们先了解VFS的几个概念。分别是进程…

sql2005 获取表字段信息和视图字段信息

获取表字段名,和字段说明SELECT[Table Name]OBJECT_NAME(c.object_id), [ColumnName]c.name, [Description]ex.value FROMsys.columns c LEFTOUTERJOINsys.exte…

解析css之position

CSS的很多其他属性大多容易理解&#xff0c;比如字体&#xff0c;文本&#xff0c;背景等。有些CSS书籍也会对这些简单的属性进行大张旗鼓的介绍&#xff0c;而偏偏忽略了对一些难缠的属性讲解&#xff0c;有避重就轻的嫌疑。CSS中主要难以理解的属性包括盒型结构&#xff0c;以…

Java ObjectInputStream readLong()方法(带示例)

ObjectInputStream类readLong()方法 (ObjectInputStream Class readLong() method) readLong() method is available in java.io package. readLong()方法在java.io包中可用。 readLong() method is used to read 8 bytes (i.e. 64 bit) of long value from this ObjectInputSt…

交换瓶子(蓝桥杯)

有N个瓶子&#xff0c;编号 1 ~ N&#xff0c;放在架子上。 比如有5个瓶子&#xff1a; 2 1 3 5 4 要求每次拿起2个瓶子&#xff0c;交换它们的位置。 经过若干次后&#xff0c;使得瓶子的序号为&#xff1a; 1 2 3 4 5 对于这么简单的情况&#xff0c;显然&#xff0c;至少…

Linux设备驱动开发---字符设备驱动程序

字符设备驱动程序1 主设备和次设备的概念设备号的注册和释放静态方法动态方法区别2 设备文件操作struct file_operations与struct file、struct inode关系3 分配和注册字符设备class_createcdev_adddevice_create4 字符设备驱动程序字符设备通过字符&#xff08;一个接一个的字…

Java LinkedHashMap getOrDefault()方法与示例

LinkedHashMap类的getOrDefault()方法 (LinkedHashMap Class getOrDefault() method) getOrDefault() method is available in java.util package. getOrDefault()方法在java.util包中可用。 getOrDefault() method is used to get the value associated with the given key el…

Java中的异常栈轨迹和异常链

Java中允许对异常进行再次抛出&#xff0c;以提交给上一层进行处理&#xff0c;最为明显的例子为Java的常规异常。 常规异常&#xff1a;有Java所定义的异常&#xff0c;不需要异常声明&#xff0c;在未被try-catch的情况下&#xff0c;会被默认上报到main()方法。 Example: pu…

贪心算法---背包问题(物品可以分割问题)

问题背景&#xff1a; 有一天&#xff0c;阿里巴巴赶着一头毛驴上山砍柴。砍好柴准备下山时&#xff0c;远处突然出现一股烟尘&#xff0c;弥漫着直向上空飞扬&#xff0c;朝他这儿卷过来&#xff0c;而且越来越近。靠近以后&#xff0c;他才看清原来是一支马队&#xff0c;他…

同步---信号量

信号量1 信号量2 驱动程序和测试程序3 内核的具体实现总结1 信号量 Linux中的信号量是一种睡眠锁。如果有一个任务试图获得一个已经被占用的信号量时&#xff0c;信号量会将其放到一个等待队列&#xff0c;然后让其睡眠&#xff0c;这时处理器去执行其他代码。当持有信号量的进…

Java Float类floatToIntBits()方法与示例

Float类floatToIntBits()方法 (Float class floatToIntBits() method) floatToIntBits() method is available in java.lang package. floatToIntBits()方法在java.lang包中可用。 floatToIntBits() method follows IEEE 754 floating-point standards and according to standa…

解释三度带和六度带的概念以及各坐标系如何定义

★ 地形图坐标系&#xff1a;我国的地形图采用高斯&#xff0d;克吕格平面直角坐标系。在该坐标系中&#xff0c;横轴&#xff1a;赤道&#xff0c;用&#xff39;表示&#xff1b;纵轴&#xff1a;中央经线&#xff0c;用&#xff38;表示&#xff1b;坐标原点&#xff1a;中央…

0-1背包问题(物品不可分割)

问题背景&#xff1a; 所谓“钟点秘书”&#xff0c;是指年轻白领女性利用工余时间为客户提供秘书服务&#xff0c;并按钟点收取酬金。“钟点秘书”为客户提供有偿服务的方式一般是&#xff1a;采用电话、电传、上网等“遥控”式 服务&#xff0c;或亲自到客户公司处理部分业务…

算法---KMP算法

字符串1 KMP算法状态机概述构建状态转移1 KMP算法 原文链接&#xff1a;https://zhuanlan.zhihu.com/p/83334559 先约定&#xff0c;本文用pat表示模式串&#xff0c;长度为M&#xff0c;txt表示文本串&#xff0c;长度为N&#xff0c;KMP算法是在txt中查找子串pat&#xff0…

cache初接触,并利用了DataView

我们在写代码的时候,如果数据控件要获得数据,一般方法,Conn.Open();OleDbCommand cmd;cmd new OleDbCommand(sql, Conn);GridView1.DataSource dbcenter.accessGetDataSet(sql);GridView1.DataBind();Conn.close();但如果多个数据控件要绑定数据,则比较频繁打开数据库,效率一…

Java ByteArrayInputStream reset()方法及示例

ByteArrayInputStream类reset()方法 (ByteArrayInputStream Class reset() method) reset() method is available in java.util package. reset()方法在java.util包中可用。 reset() method is used to reset this ByteArrayInputStream to the last time marked position and …

回文数猜想

问题描述&#xff1a; 一个正整数&#xff0c;如果从左向右读&#xff08;称之为正序数&#xff09;和从右向左读&#xff08;称之为倒序数&#xff09;是一样的&#xff0c;这样的数就叫回文数。任取一个正整数&#xff0c;如果不是回文数&#xff0c;将该数与他的倒序数相加…

文件上传 带进度条(多种风格)

文件上传 带进度条 多种风格 非常漂亮&#xff01; 友好的提示 以及上传验证&#xff01; 部分代码&#xff1a; <form id"form1" runat"server"><asp:ScriptManager ID"scriptManager" runat"server" EnablePageMethods&quo…