老大让用webbrowser控件来自动输入银行登陆界面,webbrowser结合HtmlDocument可以搞定普通文本框的输入。
好了,大功告成。
#region 输入用户名abc
HtmlDocument cd = webBrowser1.Document;
HtmlElement element = webBrowser1.Document.GetElementById("txt_username_79443");
if (element != null)
{
element.InnerText = "abc";
}
#endregion
但是密码框是安全控件,思考了半天,经高人指点,发现只能通过两种方法来输入:第一种是分析http协议(感觉就超级累的活,所以就放弃了)。
第二种就是现在我用的方法,就是用窗口探测器探测窗口,分析清楚层级关系之后,用winapi函数来控制。
模拟键盘输入需要的api函数,主要就是获取句柄,操作句柄,以及发消息的方法。
#region 模拟键盘输入密码用到的api函数
private IntPtr pwdHandle;
[DllImport("User32.dll", EntryPoint = "FindWindow")]//EntryPoint为要调用的dll的入口点名称
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);//extern用于声明在外部实现的方法
[DllImport("User32.dll", EntryPoint = "FindWindowEx")]//找子窗体
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpsazClass, string lpszWindow);
[DllImport("User32.dll", EntryPoint = "SendMessage")]//用于发送消息给窗体
private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, string lParam);
/// <summary>
/// 用于枚举子窗体的委托
/// </summary>
/// <param name="WindowHandle">窗体句柄</param>
/// <param name="num">自定义</param>
/// <returns></returns>
public delegate bool EnumChildWindow(IntPtr WindowHandle, string num);
/// <summary>
/// 获取指定窗体的所有子窗体
/// </summary>
/// <param name="WinHandle">窗体句柄</param>
/// <param name="ecw">回调委托</param>
/// <param name="name">自定义</param>
/// <returns></returns>
[DllImport("User32.dll")]
public static extern int EnumChildWindows(IntPtr WinHandle, EnumChildWindow ecw, string name);
[DllImport("user32.dll")]
public static extern int GetClassName(IntPtr WinHandle, StringBuilder Type, int size);
IntPtr mainHwnd = IntPtr.Zero;//登录窗口句柄
string typeName = string.Empty;//启动程序的窗口标题
/// <summary>
/// 枚举窗体的回调函数
/// </summary>
/// <param name="handle"></param>
/// <param name="num"></param>
/// <returns></returns>
private bool EnumChild(IntPtr handle, string num)
{
pwdHandle = handle;
return true;
}
#endregion
注意这里的枚举函数,需要用一个代理,然后写个回调函数(终于基本弄清楚代理的含义了)
下面就是使用这些函数来查找窗体句柄,并用sendmessage发送消息来控制了。
#region 输入密码
var ctl = FindWindowEx(webBrowser1.Handle, IntPtr.Zero, "Shell Embedding", null);
if (ctl == IntPtr.Zero) { MessageBox.Show("获取句柄失败!"); return; }
ctl = FindWindowEx(ctl, IntPtr.Zero, "Shell DocObject View", null);
if (ctl == IntPtr.Zero) { MessageBox.Show("获取句柄失败!"); return; }
ctl = FindWindowEx(ctl, IntPtr.Zero, "Internet Explorer_Server", null);
if (ctl == IntPtr.Zero) { MessageBox.Show("获取句柄失败!"); return; }
//枚举子窗体
EnumChildWindow ecw = EnumChild;
EnumChildWindows(ctl, ecw, "");
if (ctl == IntPtr.Zero) { MessageBox.Show("获取句柄失败!"); return; }
//弹出classname,看句柄是否正确
var cn = new StringBuilder(100);
GetClassName(pwdHandle, cn, 100);
MessageBox.Show(cn.ToString(), "密码控件classname");
//点击左键,获取输入焦点
SendMessage(pwdHandle, 0x201, IntPtr.Zero, null);//0x201鼠标左键点击
//输入密码(abc)
SendMessage(pwdHandle, 0x0100, (IntPtr)0x41, null);//0x0100是按键的意思,ox41是a,后面的依次类推
SendMessage(pwdHandle, 0x0100, (IntPtr)0x42, null);
SendMessage(pwdHandle, 0x0100, (IntPtr)0x43, null);
#endregion
注意,不能再webbrowser的completed事件里面写,不然始终找不到安全控件的句柄。。。
好了,大功告成。
其实想一想,如果能够破解验证码的话,就可以写个穷举循环,暴力破解登录页面了。呵呵。