平常工作的时候可能要频繁的登录系统来测试某个功能,那么就要不停的输入用户名和密码,浪费了不少时间。本篇文章将提供一种实现自动登录的方法。即:使用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