在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.mzph.cn/news/379582.shtml

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

相关文章

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

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

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

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

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

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

同步---信号量

信号量1 信号量2 驱动程序和测试程序3 内核的具体实现总结1 信号量 Linux中的信号量是一种睡眠锁。如果有一个任务试图获得一个已经被占用的信号量时&#xff0c;信号量会将其放到一个等待队列&#xff0c;然后让其睡眠&#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…

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

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

同步---自旋锁

1 自旋锁的基本概念 自旋锁最多只能被一个可执行线程持有&#xff0c;如果一个执行线程试图获得一个已经被使用的自旋锁&#xff0c;那么该线程就会一直进行自旋&#xff0c;等待锁重新可用。在任何时刻&#xff0c;自旋锁都可以防止多余一个的执行线程同时进入临界区。 Linu…

实习日志----4.播放时段参数设置

由于客户在下发广告时&#xff0c;一则广告可在多个时段播放&#xff0c;这就需要设置多个播放时段的参数。 但在这种情况下&#xff0c;我并不知道用户每次需要下发几个时段&#xff0c;所以前台不能设定死。 因此我要实现这么一个功能&#xff0c;让用户根据自己的需要来动态…

linux系统编程---线程总结

线程总结1 线程的实现线程创建线程退出线程等待线程清理2 线程的属性线程的分离线程的栈地址线程栈大小线程的调度策略线程优先级3 线程的同步互斥锁读写锁条件变量信号量线程是系统独立调度和分配的基本单位。同一进程中的多个线程将共享该进程中的全部系统资源&#xff0c;例…

如何给Linux操作系统(CentOS 7为例)云服务器配置环境等一系列东西

1.首先&#xff0c;你得去购买一个云服务器&#xff08;这里以阿里云学生服务器为例&#xff0c;学生必须实名认证&#xff09; 打开阿里云&#xff0c;搜索学生服务器点击进入即可 公网ip为连接云服务器的主机 自定义密码为连接云服务器是需要输入的密码 购买即可 点击云服…

Linux系统编程---I/O多路复用

文章目录1 什么是IO多路复用2 解决什么问题说在前面I/O模型阻塞I/O非阻塞I/OIO多路复用信号驱动IO异步IO3 目前有哪些IO多路复用的方案解决方案总览常见软件的IO多路复用方案4 具体怎么用selectpollepolllevel-triggered and edge-triggered状态变化通知(edge-triggered)模式下…

c#中textbox属性_C#.Net中的TextBox.MaxLength属性与示例

c#中textbox属性Here we are demonstrating use of MaxLength property of TextBox. 在这里&#xff0c;我们演示了TextBox的MaxLength属性的使用。 MaxLength property of TextBox is used to set maximum number of character that we can input into a TextBox. Limit of M…

IIS7 MVC网站生成、发布

(1)生成。 确保System.Web.Mvc.dll在bin目录下 (2)发布网站到文件系统 (3)在IIS中为网站添加应用程序池&#xff08;一个虚拟目录&#xff0c;一个应用程序池&#xff09; (4)添加在默认网站下添加虚拟目录 &#xff08;5&#xff09;转换为应用程序 至此&#xff0c;部署完毕 …

C语言多维数组

文章目录多维数组数组名下标指向数组的指针作为函数参数的多维数组指针数组小结多维数组 如果某个数组的维数超过1&#xff0c;它就被称为多维数组&#xff0c;例如&#xff0c;下面这个声明&#xff1a; int matrix[6][10]创建了一个包含60个元素的矩阵。但是&#xff0c;它…

fwrite函数的用法示例_C语言中的fwrite()函数(带有示例)

fwrite函数的用法示例C中的fwrite()函数 (fwrite() function in C) Prototype: 原型&#xff1a; size_t fwrite(void *buffer, size_t length, size_t count, FILE *filename);Parameters: 参数&#xff1a; void *buffer, size_t length, size_t count, FILE *filenameRetu…

伙伴算法、slab机制、内存管理函数

文章目录1 伙伴算法页框操作alloc_pages()2 slabslab机制要解决的问题使用高速缓存3 内存管理函数kmallockzallocvmallocvzalloc区别参考文章内核使用struct page结构体描述每个物理页&#xff0c;也叫页框。内核在很多情况下&#xff0c;需要申请连续的页框&#xff0c;而且数…

Javaweb---监听器

1.什么是监听器 监听器就是监听某个对象的状态变化的组件。 事件源&#xff1a;被监听的对象 ----- 三个域对象 request session servletContext 监听器&#xff1a;监听事件源对象 事件源对象的状态的变化都会触发监听器 ---- 62 注册监听器&#xff1a;将监听器与事件源进行…

Linux中的Ramdisk和Initrd

Ramdisk简介先简单介绍一下ramdisk&#xff0c;Ramdisk是虚拟于RAM中的盘(Disk)。对于用户来说&#xff0c;能把RAM disk和通常的硬盘分区&#xff08;如/dev/hda1&#xff09;同等对待来使用&#xff0c;例如&#xff1a;redice # mkfs.ext2 /dev/ram0mke2fs 1.38 (30-Jun-200…

slab下kmalloc内核函数实现

文章目录kmalloc的整体实现获取高速缓存高速缓存获取index总结https://blog.csdn.net/qq_41683305/article/details/124554490&#xff0c;在这篇文章中&#xff0c;我们介绍了伙伴算法、slab机制和常见的内存管理函数&#xff0c;接下来&#xff0c;我们看看kmalloc内核函数的…

标题:三羊献瑞

标题&#xff1a;观察下面的加法算式&#xff1a; 其中&#xff0c;相同的汉字代表相同的数字&#xff0c;不同的汉字代表不同的数字。 请你填写“三羊献瑞”所代表的4位数字&#xff08;答案唯一&#xff09;&#xff0c;不要填写任何多余内容。 思路分析&#xff1a; 首先…