重拾Csharp操控IE浏览器(一)死而不僵的IE
可以直接从第5步开始看:-)
需求:
落后的企业规定必须使用IE,用chrome无法正常显示和上传,还装了一些ACTIVEX之类插件。于是想将繁琐工作步骤自动执行。
1、弯路:
原想参考https://golangnote.com/topic/230.html 用golang Selenium WebDriver ,从国内镜像下载了DriverServer.exe,
好不容易编译成功了,又提示要java;又好不容易重新安装了jdk11,又提示IE Protection Mode之类,把IE启用保护模式选项关掉,又提示一些错误。懒得弄了。
2、具备写代码条件:
打开VS2017(社区版),又遇到许可证过期,更新许可证无效。网上找到解决方案:在D:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE下运行DDConfigCA.exe,然后在网页上先登录hotmail.com,再次更新许可证,终于将VS2017恢复正常了。
3、操控IE还是C#最在行:
其实最主要就是用SHDocVw.InternetExplorer控制IE,用 mshtml.HTMLDocument获取html元素,然后进行导航、输入、点击按钮。
4、进入正题:
VS2017新建解决方案(我是打开以前的方案),主要是先建一个winform应用(.net framework4.5.1),首先要添加引用,记得是从COM中添加了microsoft internet controls和从程序集扩展里添加了Microsoft.mshtml,并且设 嵌入互操作类型为true,参考我之前的文章:https://www.cnblogs.com/pu369/p/9068972.html 剩下的就是放置一些按钮或menuStrip,添加click事件。
5、一些实例代码:
5.1 判断VPN是否连接了(其实就是判断进程中是否有gwtrayclient)
Process[] app = Process.GetProcessesByName("XXtrayclient");
然后判断 app.Length > 0,说明有这个进程了。
5.2连接VPN(其实就是模仿登录网页,登录成功后电脑会调起XXtrayclient;其中用IE.Busy循环等待IE加载完成)
private void 公司ToolStripMenuItem_Click(object sender, EventArgs e) { SHDocVw.InternetExplorer IE = new SHDocVw.InternetExplorer(); object Empty = 0; object URL1 = "https://vpn.XXX.com.cn"; object URL2 = "https://wssvn.XXX.com.cn"; IE.Visible = true; IE.Navigate2(ref URL1, ref Empty, ref Empty, ref Empty, ref Empty); System.Threading.Thread.Sleep(2000); IE.Navigate2(ref URL2, ref Empty, ref Empty, ref Empty, ref Empty); do { System.Threading.Thread.Sleep(500); } while (IE.Busy); mshtml.HTMLDocument doc_oa = IE.Document; var inputs = doc_oa.getElementsByTagName("input"); //获取用户名输入框 //input没name属性,可以用className判断 foreach (mshtml.HTMLInputElementClass input in inputs) { var a = input.className; var b = Convert.ToString(input.getAttribute("className")); if (input.className == "login") { input.click(); } } }
5.3输入用户名密码登录
private void 公司ToolStripMenuItem_Click(object sender, EventArgs e) { try { this.WindowState = FormWindowState.Minimized; SHDocVw.InternetExplorer IE = new SHDocVw.InternetExplorer(); object Empty = 0; object URL = "http://www.xxx.com/login.jsp"; IE.Visible = true; IE.Navigate2(ref URL, ref Empty, ref Empty, ref Empty, ref Empty); do { System.Threading.Thread.Sleep(500); } while (IE.Busy); var doc_oa = (mshtml.HTMLDocument)IE.Document; var userid_oa = (mshtml.HTMLInputElementClass)doc_oa.getElementById("loginid"); //获取用户名输入框 var userpwd_oa = (mshtml.HTMLInputElementClass)doc_oa.getElementById("userpassword");//#获取密码输入框 userid_oa.value = "admin"; //#输入用户名 userpwd_oa.value = "1234";//输入密码 var btSubmit_oa = (mshtml.HTMLInputElementClass)doc_oa.getElementById("login");//获取登录按钮 btSubmit_oa.click();//点击 //关闭登录提示 System.Threading.Thread.Sleep(2000); var oa_ButtonCancel_0 = IE.Document.getElementById("_ButtonCancel_0"); oa_ButtonCancel_0.click(); } catch (Exception e1) { // MessageBox.Show("error IE" + e1.Message); } }
5.4将程序本身最小化
this.WindowState = FormWindowState.Minimized;
5.5打开记事本
System.Diagnostics.Process.Start("notepad.exe");
5.6为了方便操作已经打开的IE页面,自定义了一个iehastitle函数,用于根据部分标题查找打开的IE窗口
public SHDocVw.InternetExplorer iehastitle(string s) { //查找标题包括s的IE SHDocVw.ShellWindows windows = new SHDocVw.ShellWindows(); var title = ""; for (int i = 0; i < windows.Count; i++) { SHDocVw.InternetExplorer IE = windows.Item(i) as SHDocVw.InternetExplorer; if (IE != null) { title = IE.LocationName; if (title.Contains(s)) { return IE; } } } return null; }
5.7遇到的难点之一是frameset套frameset(其中外层frameset中有1个frame,内层frameset中有4个frame;frame中还有iframe)参考代码:
//网页是frameset套frameset,其中:外层frameset中有1个frame, //内层frameset中有4个frame(name分别是menu、list、file、rightbar); //frame[2](name为list)中还有iframe //首先找到办公自动化页面 var IE = iehastitle("办公自动化"); mshtml.HTMLDocument IE_doc = IE.Document; //当前网页中frames总数5个(即上面说的外层的1个和内层的4个共5frame,后面我称之为第0个至第4个frame,没有frameset什么事) int frame_num = IE_doc.frames.length; //item(0)在外层frameset中,称之为第0个frame,类型是HTMLWindow2 //最主要需获取的是frame[2](name为list),其中还有iframe //frame2的window mshtml.HTMLWindow2 frame2_win = IE_doc.frames.item(2) as mshtml.HTMLWindow2; //frame2的document mshtml.HTMLDocument frame2_doc = frame2_win.document as mshtml.HTMLDocument; //frame2中的iframe的window var frame2_iframe = frame2_doc.frames.item(0) as mshtml.HTMLWindow2; //iframe的document var iframe_doc = frame2_iframe.document as mshtml.HTMLDocument; var iframe_htmlstr = iframe_doc.body.innerHTML; var links = iframe_doc.getElementsByTagName("a");
可简化成类似下面的一句:
mshtml.HTMLDocument iframe_doc = (((IE.Document.frames.item(2) as mshtml.HTMLWindow2).document as mshtml.HTMLDocument).frames.item(0) as mshtml.HTMLWindow2).document as mshtml.HTMLDocument;
5.8难点之二是confirm对话框和alert对话框的关闭,在webBrowser1_Navigated或webBrowser1_DocumentCompleted的时候用doc.parentWindow.execScript执行javascript脚本;还可参考https://www.cnblogs.com/qqhfeng/p/4194938.html,禁止脚本错误。
//注意:一定要放到你的代码执行前面,这样有弹出框的时候才会自动点击,如果把这段代码放到你点击按钮之后,点击按钮弹出的提示框是自动点击不了的 doc.parentWindow.execScript("window.openr=null;function confirm(str){return true;} ", "javascript"); //弹出确认 doc.parentWindow.execScript("window.openr=null;function alert(str){return true;} ", "javaScript");//弹出提示
5.9启动线程(其中gethrp可以是自定义的 private static void gethrp()函数)
Thread thread = new Thread(new ThreadStart(gethrp)); //启动线程 thread.Start();
5.10存盘
//写盘 if (info != "") { using (StreamWriter sw = new StreamWriter(@"D:\hrp.txt", true)) { sw.WriteLine(info); } }
5.11读取文档
using (StreamReader sr = new StreamReader(@"E:\memo.txt")) { this.textBox1.Text = sr.ReadToEnd(); }
5.12将form显示在最上层
this.TopMost = true;
5.13执行adb命令打开手机电源
private void adbToolStripMenuItem_Click(object sender, EventArgs e) { String exe = Application.StartupPath + "\\adb\\adb.exe"; Process p = new Process(); p.StartInfo.FileName = exe; //设定程序名 p.StartInfo.UseShellExecute = false; //关闭Shell的使用 p.StartInfo.RedirectStandardInput = true; //重定向标准输入 p.StartInfo.RedirectStandardOutput = true; //重定向标准输出 p.StartInfo.RedirectStandardError = true; //重定向错误输出 p.StartInfo.CreateNoWindow = true; //设置不显示窗口 p.StartInfo.Arguments = "devices"; //设定程式执行參數 p.Start(); this.label1.Text = p.StandardOutput.ReadToEnd(); p.StartInfo.Arguments = "shell input keyevent 26"; //设定程式执行參數 p.Start(); if (p != null) { p.Close(); } }
5.14下载网络图片
//DownloadImage(url, @"d:\a.jpg"); public void DownloadImage(string url, string path) { HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); req.ServicePoint.Expect100Continue = false; req.Method = "GET"; req.KeepAlive = true; req.ContentType = "image/*"; HttpWebResponse rsp = (HttpWebResponse)req.GetResponse(); System.IO.Stream stream = null; try { // 以字符流的方式读取HTTP响应 stream = rsp.GetResponseStream(); Image.FromStream(stream).Save(path); } finally { // 释放资源 if (stream != null) stream.Close(); if (rsp != null) rsp.Close(); } }
5.15根据url找已经存在的IE
public SHDocVw.InternetExplorer iehasurl(string s) { //查找url包括s的IE SHDocVw.ShellWindows windows = new SHDocVw.ShellWindows(); var url = ""; for (int i = 0; i < windows.Count; i++) { SHDocVw.InternetExplorer IE = windows.Item(i) as SHDocVw.InternetExplorer; if (IE != null) { url = IE.LocationURL; if (url.Contains(s)) { return IE; } } } return null; }