当前位置:首页 > WinForm

使用EnumChildWindows和SendMessage模拟鼠标键盘实现自动登录

发表于 2017-06-15 23:39

平常工作的时候可能要频繁的登录系统来测试某个功能,那么就要不停的输入用户名和密码,浪费了不少时间。本篇文章将提供一种实现自动登录的方法。即:使用EnumChildWindows和SendMessage来实现,写一个小的Exe通过EnumChildWindows来查找的登录窗口上各个控件的句柄,然后使用SendMessage向目标控件发送消息来模拟鼠标键盘。

准备

为了演示,我先做了一个很简单的程序来模拟我们的系统,该程序只有一个登录界面,登录成功后会弹出一个主界面。可执行程序名为WinditeProgram.exe,如图:

 login.PNG

main.PNG

 

实现功能

我们将要实现的功能是自动打开程序,并且自动输入用户名、密码,自动点击登录按钮,实现自动登录功能。

 

步骤

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++来查看登录窗口中,用户名文本框、密码文本框、登录按钮的在此窗口上的顺序。如:
loginspy.PNG

可以很清楚的知道,用户名文本框是在第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. 至此自动登录的功能已经完成,大家可以下载源码查看效果。


源码下载:

AutoLogin20170615


本文章由创风网原创,转载请注明出处:http://www.windite.com/article/details/2yr6ea17