平常工作的时候可能要频繁的登录系统来测试某个功能,那么就要不停的输入用户名和密码,浪费了不少时间。本篇文章将提供一种实现自动登录的方法。即:使用EnumChildWindows和SendMessage来实现,写一个小的Exe通过EnumChildWindows来查找的登录窗口上各个控件的句柄,然后使用SendMessage向目标控件发送消息来模拟鼠标键盘。
准备
为了演示,我先做了一个很简单的程序来模拟我们的系统,该程序只有一个登录界面,登录成功后会弹出一个主界面。可执行程序名为WinditeProgram.exe,如图:
实现功能
我们将要实现的功能是自动打开程序,并且自动输入用户名、密码,自动点击登录按钮,实现自动登录功能。
步骤
1. 新建一个Windows应用程序的项目,名为AutoLogin。先定义好要使用到的API函数等。
public delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam); [DllImport("user32.dll", EntryPoint = "SendMessage")] private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam); [DllImport("user32.dll", EntryPoint = "SendMessage")] private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, string lParam); [DllImport("user32.dll", EntryPoint = "GetWindowText", ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpWindowText, int nMaxCount); private const int WM_SETTEXT = 0x000C; private const int BM_CLICK = 0x00F5;
2. 我们要使用Spy++来查看登录窗口中,用户名文本框、密码文本框、登录按钮的在此窗口上的顺序。如:
可以很清楚的知道,用户名文本框是在第3个,密码文本框是在第1个,登录按钮是在第5个。
private const int TEXTBOX_USERID_SEQUENCE = 3; private const int TEXTBOX_PASSWORD_SEQUENCE = 1; private const int BUTTON_LOGIN_SEQUENCE = 5;
3. 将WinditeProgram.exe放在自动登录程序项目生成目录下,以便打开自动登录程序时可以启动它。启动后,通过进程可以获取到WinditeProgram的登录窗口的句柄,然后通过EnumChildWindows来遍历登录窗口上的子控件,之前我们已经知道想要的控件在第几个位置,所以我们可以获取到它们的句柄,然后用Dictionary保存起来。最后通过SendMessage发送WM_SETTEXT消息模拟键盘填上用户名和密码,发送BM_CLICK消息模拟鼠标点击登录按钮。
[STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); string exePath = System.IO.Path.Combine(Application.StartupPath, "WinditeProgram.exe"); System.Diagnostics.Process process = null; process = System.Diagnostics.Process.Start(exePath); _sequence = 0; StringBuilder sb = new StringBuilder(); int totalTryTime = 100; int tryTime = 0; // 此代码为了防止目标程序启动过慢,没有获取到正确的MainWindowHandle GetWindowText(process.MainWindowHandle, sb, 1024); while (sb.ToString().ToUpper() != "Login".ToUpper()) { tryTime += 1; if (tryTime > totalTryTime) { MessageBox.Show("查找窗口超时。"); return; } System.Threading.Thread.Sleep(100); GetWindowText(process.MainWindowHandle, sb, 1024); } Dictionaryresult = new Dictionary(); GCHandle listHandle = GCHandle.Alloc(result); try { EnumWindowsProc childProc = new EnumWindowsProc(EnumWindow); EnumChildWindows(process.MainWindowHandle, childProc, GCHandle.ToIntPtr(listHandle)); } finally { if (listHandle.IsAllocated) { listHandle.Free(); } } if (result.Count == 3) { SendMessage(result[TEXTBOX_USERID_SEQUENCE],WM_SETTEXT,0,USERID); SendMessage(result[TEXTBOX_PASSWORD_SEQUENCE],WM_SETTEXT,0,PASSWORD); SendMessage(result[BUTTON_LOGIN_SEQUENCE],BM_CLICK,0,0); } } private static bool EnumWindow(IntPtr handle, IntPtr pointer) { GCHandle gch = GCHandle.FromIntPtr(pointer); Dictionarydict = gch.Target as Dictionary; if (dict == null) { return false; } _sequence += 1; if (_sequence == TEXTBOX_USERID_SEQUENCE) { dict[TEXTBOX_USERID_SEQUENCE] = handle; } else if (_sequence == TEXTBOX_PASSWORD_SEQUENCE) { dict[TEXTBOX_PASSWORD_SEQUENCE] = handle; } else if (_sequence == BUTTON_LOGIN_SEQUENCE) { dict[BUTTON_LOGIN_SEQUENCE] = handle; } if (dict.Count == 3) { return false; } return true; }
注意:SetWindowText只能在本进程内对文本框设置Text,跨进程需要使用SendMessage。
4. 至此自动登录的功能已经完成,大家可以下载源码查看效果。
源码下载:
本文章由创风网原创,转载请注明出处:http://www.windite.com/article/details/2yr6ea17