前两天去面试一公司,流程的最后面我主管让我上机,原题说是自动打开浏览器去点某个页面,我说可以打开浏览器进程去xxxx...,然而他的本意并非如此。后来我总算听懂了,大意是做一个类似于“按键精灵”的功能,题目则改为移动鼠标去双击点开某个qq好友进入聊天对话框,允许上网查资料。
好在我查到了这篇https://www.2cto.com/kf/201410/343342.html
关键代码均来自源于此,稍微改了一下。
//1、windowsApi.cs
1 using System; 2 using System.Runtime.InteropServices; 3 4 namespace demo 5 { 6 //结构体布局 本机位置 7 [StructLayout(LayoutKind.Sequential)] 8 public struct NativeRECT 9 { 10 public int left; 11 public int top; 12 public int right; 13 public int bottom; 14 } 15 16 //将枚举作为位域处理 17 [Flags] 18 public enum MouseEventFlag : uint //设置鼠标动作的键值 19 { 20 Move = 0x0001, //发生移动 21 LeftDown = 0x0002, //鼠标按下左键 22 LeftUp = 0x0004, //鼠标松开左键 23 RightDown = 0x0008, //鼠标按下右键 24 RightUp = 0x0010, //鼠标松开右键 25 MiddleDown = 0x0020, //鼠标按下中键 26 MiddleUp = 0x0040, //鼠标松开中键 27 XDown = 0x0080, 28 XUp = 0x0100, 29 Wheel = 0x0800, //鼠标轮被移动 30 VirtualDesk = 0x4000, //虚拟桌面 31 Absolute = 0x8000 32 } 33 class WindowsAPI 34 { 35 //设置鼠标位置 36 [DllImport("user32.dll")] 37 public static extern bool SetCursorPos(int X, int Y); 38 39 //设置鼠标按键和动作 40 [DllImport("user32.dll")] 41 public static extern void mouse_event(MouseEventFlag flags, int dx, int dy, 42 uint data, UIntPtr extraInfo); //UIntPtr指针多句柄类型 43 44 [DllImport("user32.dll")] 45 public static extern IntPtr FindWindow(string strClass, string strWindow); 46 47 //该函数获取一个窗口句柄,该窗口雷鸣和窗口名与给定字符串匹配 hwnParent=Null从桌面窗口查找 48 [DllImport("user32.dll")] 49 public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, 50 string strClass, string strWindow); 51 52 [DllImport("user32.dll")] 53 public static extern bool GetWindowRect(HandleRef hwnd, out NativeRECT rect); 54 } 55 }
//2、业务逻辑
1 using System; 2 using System.Runtime.InteropServices; 3 using System.Windows.Forms; 4 5 namespace demo 6 { 7 public partial class Form1 : Form 8 { 9 public Form1() 10 { 11 InitializeComponent(); 12 } 13 14 private void button1_Click(object sender, EventArgs e) 15 { 16 NativeRECT rect; 17 //获取主窗体句柄 18 IntPtr ptrDeskTopbar = WindowsAPI.FindWindow("Shell_TrayWnd", null); 19 if (ptrDeskTopbar == IntPtr.Zero) 20 { 21 MessageBox.Show("No windows found!"); 22 return; 23 } 24 25 //获取子窗体 26 IntPtr childPtr = WindowsAPI.FindWindowEx(ptrDeskTopbar, IntPtr.Zero, "TrayNotifyWnd", null); 27 if (childPtr == IntPtr.Zero) 28 { 29 MessageBox.Show("No child windows found!"); 30 return; 31 } 32 33 IntPtr ccPtr = WindowsAPI.FindWindowEx(childPtr, IntPtr.Zero, "SysPager", null); 34 if (ccPtr == IntPtr.Zero) 35 { 36 MessageBox.Show("No childschild windows found!"); 37 return; 38 } 39 40 //获取窗体大小 41 WindowsAPI.GetWindowRect(new HandleRef(this, ccPtr), out rect); 42 int X = rect.left+14; 43 int Y =rect.top+15 ; 44 45 WindowsAPI.SetCursorPos(X, Y); 46 run_doubleClick(); 47 48 //获取qq页面的主页面句柄 49 IntPtr qqPtr = WindowsAPI.FindWindow("TXGuiFoundation", "QQ"); 50 if (qqPtr == IntPtr.Zero) 51 { 52 MessageBox.Show("No qq windows found!"); 53 return; 54 } 55 //获取窗体大小 56 WindowsAPI.GetWindowRect(new HandleRef(this, qqPtr), out rect); 57 X = rect.left + 67; 58 Y = rect.top + 278; 59 60 WindowsAPI.SetCursorPos(X, Y); 61 run_Click(); 62 63 X = rect.left + 96; 64 Y = rect.top + 317; 65 66 WindowsAPI.SetCursorPos(X, Y); 67 run_doubleClick(); 68 } 69 70 71 /// <summary> 72 /// 模拟单击 73 /// </summary> 74 void run_Click() 75 { 76 WindowsAPI.mouse_event(MouseEventFlag.LeftDown, 0, 0, 0, UIntPtr.Zero); 77 WindowsAPI.mouse_event(MouseEventFlag.LeftUp, 0, 0, 0, UIntPtr.Zero); 78 } 79 80 /// <summary> 81 /// 模拟双击 82 /// </summary> 83 void run_doubleClick() 84 { 85 WindowsAPI.mouse_event(MouseEventFlag.LeftDown, 0, 0, 0, UIntPtr.Zero); 86 WindowsAPI.mouse_event(MouseEventFlag.LeftUp, 0, 0, 0, UIntPtr.Zero); 87 WindowsAPI.mouse_event(MouseEventFlag.LeftDown, 0, 0, 0, UIntPtr.Zero); 88 WindowsAPI.mouse_event(MouseEventFlag.LeftUp, 0, 0, 0, UIntPtr.Zero); 89 } 90 } 91 }
关键的API解释:
public static extern IntPtr FindWindow(string strClass, string strWindow); public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,string strClass, string strWindow);
FindWindow:根据窗体的类名和标题来获取窗体的句柄;
FindWindowEx:根据主窗口(可为空,为空时默认主窗口为桌面)句柄 和子窗口句柄(可为空),及类名和标题名(可为null)获取子窗口句柄。
其中类名和标题名可通过按键精灵来抓取,如下图: