重拾Csharp操控IE浏览器(二)功夫还在IE外
上篇文章https://www.cnblogs.com/pu369/p/12343259.html主要是对SHDocVw.InternetExplorer的控制,然而有时还需一些win32API才能真正实际完全自动化操作(必要时用spy++查看窗体层次)。
一些DllImport(在 public partial class Form1 : Form中):
[DllImport("user32.dll")] private static extern bool EnumWindows(WNDENUMPROC lpEnumFunc, int lParam); private delegate bool WNDENUMPROC(IntPtr hWnd, int lParam); //寻找目标进程窗口 [DllImport("User32.dll", EntryPoint = "FindWindow")] public extern static IntPtr FindWindow(string lpClassName, string lpWindowName); //设置进程窗口到最前 [DllImport("USER32.DLL")] public static extern bool SetForegroundWindow(IntPtr hWnd); //模拟键盘事件 [DllImport("USER32.DLL")] public static extern void keybd_event(Byte bVk, Byte bScan, Int32 dwFlags, Int32 dwExtraInfo); public delegate bool CallBack(IntPtr hwnd, int lParam); [DllImport("USER32.DLL")] public static extern int EnumChildWindows(IntPtr hWndParent, CallBack lpfn, int lParam); public delegate bool EnumChildWindow(IntPtr WindowHandle, string num); //给CheckBox发送信息 [DllImport("USER32.DLL", EntryPoint = "SendMessage", SetLastError = true, CharSet = CharSet.Auto)] public static extern int SendMessage(IntPtr hwnd, UInt32 wMsg, int wParam, int lParam); //给Text发送信息 [DllImport("USER32.DLL", EntryPoint = "SendMessage")] private static extern int SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, string lParam); [DllImport("USER32.DLL")] public static extern IntPtr GetWindow(IntPtr hWnd, int wCmd); [DllImport("User32.dll", EntryPoint = "FindWindowEx")] public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpClassName, string lpWindowName); [DllImport("user32.dll")] private static extern int GetWindowTextW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)]StringBuilder lpString, int nMaxCount); [DllImport("user32.dll")] private static extern int GetClassNameW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)]StringBuilder lpString, int nMaxCount); //函数原型;DWORD GetWindowThreadProcessld(HWND hwnd,LPDWORD lpdwProcessld); //参数: //hWnd:窗口句柄。 //lpdwProcessld:接收进程标识的32位值的地址。如果这个参数不为NULL,GetWindwThreadProcessld将进程标识拷贝到这个32位值中,否则不拷贝。 //返回值:返回值为创建窗口的线程标识。 //C#中使用该函数首先导入命名空间:using System.Runtime.InteropServices;然后写API引用部分的代码,放入 class 内部 [DllImport("User32.dll", CharSet = CharSet.Auto)] public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID);
1、寻找系统的全部窗体
static WindowInfo[] GetAllDesktopWindows() { List<WindowInfo> wndList = new List<WindowInfo>(); EnumWindows(delegate(IntPtr hWnd, int lParam) { WindowInfo wnd = new WindowInfo(); StringBuilder sb = new StringBuilder(256); //get hwnd wnd.hWnd = hWnd; //get window name GetWindowTextW(hWnd, sb, sb.Capacity); wnd.szWindowName = sb.ToString(); //get window class GetClassNameW(hWnd, sb, sb.Capacity); wnd.szClassName = sb.ToString(); Console.WriteLine("Window handle=" + wnd.hWnd.ToString().PadRight(20) + " szClassName=" + wnd.szClassName.PadRight(20) + " szWindowName=" + wnd.szWindowName); //add it into list wndList.Add(wnd); return true; }, 0); return wndList.ToArray(); }
//保存窗体信息的struct public struct WindowInfo { public IntPtr hWnd; public string szWindowName; public string szClassName; }
用上面的代码,实现获取所有窗体信息
private void testToolStripMenuItem_Click_2(object sender, EventArgs e) { var all = GetAllDesktopWindows(); string s="" ; foreach(var b in all) { s = s +b.hWnd.ToString() + " ||| " + b.szClassName +" ||| " +b.szWindowName +"\r\n"; } using (StreamWriter sw = new StreamWriter(@"temp.txt", true)) { sw.WriteLine(s); } MessageBox.Show("所有窗体信息保存到当前目录temp.txt "); }
用上面的代码,实现根据部分标题找窗体
private void 根据部分标题找窗口ToolStripMenuItem_Click(object sender, EventArgs e) { MessageBox.Show("请设置断点查看"); //枚举窗口,并显示名称和hWnd List<string> result = new List<string>(); WindowInfo[] a = GetAllDesktopWindows(); int i = 0; string s = this.textBox1.Text.Trim(); for (i = 0; i < a.Length; i++) { if (a[i].szWindowName.Contains(s)) { string msg = "hwnd:" + a[i].hWnd + " 窗口类:" + a[i].szClassName + " 标题:" + a[i].szWindowName; // MessageBox.Show(msg); result.Add(msg); } } //请在下一行设置断点 }
2、打开另一个窗体Form
private void button1_Click(object sender, EventArgs e) { Form2 f2 = new Form2(); f2.Show(); }
3、枚举电脑上所有进程
private void processToolStripMenuItem_Click_1(object sender, EventArgs e) { MessageBox.Show("请设置断点查看"); //枚举进程名和_Handle string s = ""; Process[] app = Process.GetProcesses(); List<string> li = new List<string>(); foreach (Process p in app) { try { s = "进程名:" + p.ProcessName + " ______________ 进程句柄_Handle:" + p.Handle; li.Add(s); } catch (Exception e1) { e.ToString(); } } var aproess = li; //请在下一行设置断点 }
4、用FindWindow根据主窗口标题查找窗体hwnd,再用GetWindowThreadProcessId根据hwnd找进程ID,再用Process.GetProcessById(进程ID).MainWindowTitle得到主窗口标题
private void poocessIDToolStripMenuItem1_Click(object sender, EventArgs e) { //获取计算器窗口句柄 ,需要先打开计算器 IntPtr hwnd = FindWindow(null, "计算器"); if (hwnd != IntPtr.Zero) { int calcID; //获取进程ID GetWindowThreadProcessId(hwnd, out calcID); MessageBox.Show("FindWindow找出计算器hwnd,再GetWindowThreadProcessId得到进程ID:" + calcID.ToString()); string t = Process.GetProcessById(calcID).MainWindowTitle; MessageBox.Show("GetProcessById得到计算器MainWindowTitle:" + t); } else { MessageBox.Show("没有找到计算器窗口"); } }
5、控制计算器做加法
private void testToolStripMenuItem_Click_2(object sender, EventArgs e) { //根据输入框内容,找有窗口标题符合的窗口 string title = "计算器"; //获取窗口句柄 IntPtr hwnd = FindWindow(null, title); if (hwnd != IntPtr.Zero) { SetForegroundWindow(hwnd); System.Windows.Forms.SendKeys.SendWait("12"); System.Threading.Thread.Sleep(1000); System.Windows.Forms.SendKeys.SendWait("{ADD}"); System.Threading.Thread.Sleep(1000); System.Windows.Forms.SendKeys.SendWait("23"); System.Threading.Thread.Sleep(1000); //System.Windows.Forms.SendKeys.SendWait("{TAB}"); System.Windows.Forms.SendKeys.SendWait("{ENTER}"); } else { MessageBox.Show("没有找到"+title+"窗口"); } }
6、点击 windows 安全 窗口上的 确定 按钮
private void testToolStripMenuItem_Click_2(object sender, EventArgs e) { //查找Windows 安全,点确定确认证书 IntPtr hwnd = FindWindow(null, "Windows 安全"); IntPtr childHwnd = FindWindowEx(hwnd, IntPtr.Zero, "DirectUIHWND", null); IntPtr childHwnd3_1 = FindWindowEx(childHwnd, IntPtr.Zero, "CtrlNotifySink", null); IntPtr childHwnd3_2 = FindWindowEx(childHwnd, childHwnd3_1, "CtrlNotifySink", null); IntPtr childHwnd3_3 = FindWindowEx(childHwnd, childHwnd3_2, "CtrlNotifySink", null); IntPtr childHwnd3_3_ok = FindWindowEx(childHwnd3_3, IntPtr.Zero, null, null); //uint WM_SETTEXT = 0xC; //SendMessage(editor, WM_SETTEXT, IntPtr.Zero, "username"); uint BM_CLICK = 0x00F5; SendMessage(childHwnd3_3_ok, BM_CLICK, 0, 0); //发送点击按钮的消息 }
7、批量生成目录
private void 生成目录名ToolStripMenuItem1_Click(object sender, EventArgs e) { List<string> lis = new List<string>() { "目录1", "目录2", "目录3" }; foreach (string li in lis) { if (!Directory.Exists(@"Y:\" + li)) { Directory.CreateDirectory(@"Y:\" + li); } } }
8、窗口前置
SetForegroundWindow(hwnd);
9、发邮件
private void 发邮件ToolStripMenuItem_Click(object sender, EventArgs e) { //实例化一个发送邮件类。 MailMessage mailMessage = new MailMessage(); //发件人邮箱地址,方法重载不同,可以根据需求自行选择。 mailMessage.From = new MailAddress("XXX@qq.com"); //收件人邮箱地址。 mailMessage.To.Add(new MailAddress("XXX@qq.com")); //邮件标题。 mailMessage.Subject = "发送邮件测试-发给自己"; //邮件内容。 mailMessage.Body = "这是我给我自己发送的第一份邮件哦!"; //实例化一个SmtpClient类。 SmtpClient client = new SmtpClient(); //在这里我使用的是qq邮箱,所以是smtp.qq.com,如果你使用的是126邮箱,那么就是smtp.126.com。 client.Host = "smtp.qq.com"; //使用安全加密连接。 client.EnableSsl = true; //不和请求一块发送。 client.UseDefaultCredentials = false; //验证发件人身份(发件人的邮箱,邮箱里的生成授权码); client.Credentials = new NetworkCredential("XX@qq.com", "apueXXXd"); //发送 client.Send(mailMessage); MessageBox.Show("发送成功"); }
10、鼠标模拟点击
private void testToolStripMenuItem_Click_2(object sender, EventArgs e) { MouseMove m = new MouseMove(); m.MouseMoveAndClick(1436, 357); }
class MouseMove { #region MouseEvent [System.Runtime.InteropServices.DllImport("user32")] private static extern int mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo); //移动鼠标 const int MOUSEEVENTF_MOVE = 0x0001; //模拟鼠标左键按下 const int MOUSEEVENTF_LEFTDOWN = 0x0002; //模拟鼠标左键抬起 const int MOUSEEVENTF_LEFTUP = 0x0004; //模拟鼠标右键按下 const int MOUSEEVENTF_RIGHTDOWN = 0x0008; //模拟鼠标右键抬起 const int MOUSEEVENTF_RIGHTUP = 0x0010; //模拟鼠标中键按下 const int MOUSEEVENTF_MIDDLEDOWN = 0x0020; //模拟鼠标中键抬起 const int MOUSEEVENTF_MIDDLEUP = 0x0040; //标示是否采用绝对坐标 const int MOUSEEVENTF_ABSOLUTE = 0x8000; #endregion //这个区域包括任务栏,就是屏幕显示的物理范围 struct ScreenWH { //非静态变量不能直接赋值 public static int x = Screen.PrimaryScreen.Bounds.Width; //屏幕宽度 public static int y = Screen.PrimaryScreen.Bounds.Height; //屏幕宽度 } //x,y是鼠标在屏幕的位置,屏幕左上角为0,0点 public void MouseMoveAndClick(int _x, int _y) { mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, _x * 65535 / ScreenWH.x, _y * 65535 / ScreenWH.y, 0, 0); mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); } }
11、模拟键盘CTRL+V粘贴操作
private void textBox1_DoubleClick(object sender, EventArgs e) { Clipboard.SetDataObject("HELLO WORLD!!!", true); KeyBordClick k = new KeyBordClick(); k.KeyBordPaste(); }
class KeyBordClick { [DllImport("user32.dll", EntryPoint = "keybd_event", SetLastError = true)] private static extern void keybd_event(Keys bVk, byte bScan, uint dwFlags, uint dwExtraInfo); const int KEYEVENTF_KEYUP = 0x02; const int KEYEVENTF_KEYDOWN = 0x00; //相当于按下ctrl+v,然后回车 public void KeyBordPaste() { keybd_event(Keys.ControlKey, 0, KEYEVENTF_KEYDOWN, 0); keybd_event(Keys.V, 0, 0, 0); keybd_event(Keys.ControlKey, 0, KEYEVENTF_KEYUP, 0); keybd_event(Keys.Enter, 0, 0, 0); } }
12、屏幕截图,并设为当前form的背景
private void testToolStripMenuItem_Click_2(object sender, EventArgs e) { this.BackgroundImage = CaptureScreen.CaptureScreenA(); }
class CaptureScreen { /// <summary> /// 截屏幕成为图像返回 /// </summary> /// <returns></returns> public static Image CaptureScreenA() { //屏幕宽 int iWidth = Screen.PrimaryScreen.Bounds.Width; //屏幕高 int iHeight = Screen.PrimaryScreen.Bounds.Height; //按照屏幕宽高创建位图 Image img = new Bitmap(iWidth, iHeight); //从一个继承自Image类的对象中创建Graphics对象 Graphics gc = Graphics.FromImage(img); //抓屏并拷贝到myimage里 gc.CopyFromScreen(new Point(0, 0), new Point(0, 0), new Size(iWidth, iHeight)); //保存位图 //img.Save(@"E:\" + Guid.NewGuid().ToString() + ".jpg"); return img; } }
13、图片剪裁类
public class ImageManager { /// <summary> /// 图像切割 /// </summary> /// <param name="url">图像文件名称</param> /// <param name="width">切割后图像宽度</param> /// <param name="height">切割后图像高度</param> /// <param name="savePath">切割后图像文件保存路径</param> /// <param name="fileExt">切割后图像文件扩展名</param> public static void Cut(string url, int width, int height, string savePath, string fileExt, string logofile) { Bitmap bitmap = new Bitmap(url); Decimal MaxRow = Math.Ceiling((Decimal)bitmap.Height / height); Decimal MaxColumn = Math.Ceiling((decimal)bitmap.Width / width); for (decimal i = 0; i < MaxRow; i++) { for (decimal j = 0; j < MaxColumn; j++) { string filename = i.ToString() + "," + j.ToString() + "." + fileExt; Bitmap bmp = new Bitmap(width, height); for (int offsetX = 0; offsetX < width; offsetX++) { for (int offsetY = 0; offsetY < height; offsetY++) { if (((j * width + offsetX) < bitmap.Width) && ((i * height + offsetY) < bitmap.Height)) { bmp.SetPixel(offsetX, offsetY, bitmap.GetPixel((int)(j * width + offsetX), (int)(i * height + offsetY))); } } } Graphics g = Graphics.FromImage(bmp); g.DrawString("脚本之家", new Font("黑体", 20), new SolidBrush(Color.FromArgb(70, Color.WhiteSmoke)), 60, height / 2);//加水印 ImageFormat format = ImageFormat.Png; switch (fileExt.ToLower()) { case "png": format = ImageFormat.Png; break; case "bmp": format = ImageFormat.Bmp; break; case "gif": format = ImageFormat.Gif; break; } bmp.Save(savePath + "//" + filename, format); } } } }