业务场景
最近接到一个需求,向一个程序(简称A程序)指定的文本框输入数据。其实是一个扫码输入场景,之前是通过手持扫码枪通过USB连接电脑,然后光标放入A程序文本框以后,手动扫码就可以输入了。这个需求是通过自动扫码设备,读取条码数据自动输入到A程序的文本框。考虑到有几种解决方案,有自动化脚本,实现自动点击自动输入,其实作为我这个场景,更多的需求是用户体验,无感对接。经过比对以后,采用句柄的窗口进行键盘输入。这样的方案,主要是通过Windows API函数,获取A程序窗口句柄,然后获取文本框的具体地址,进行输入实现。这样实现方式,不影响电脑正常使用,让程序显得更加专业化。
工具选择
获取窗口句柄的工具有很多,本文精益编程助手为例。
实现过程
首先我们就要获取A程序的窗口句柄。
A程序窗口
打开精益编程助手,找到要输入的窗口。
引入Window API
[DllImport("user32.dll", EntryPoint = "GetCursorPos")]
public static extern bool GetCursorPos(out Point pt);
[DllImport("user32.dll", EntryPoint = "WindowFromPoint")]
public static extern IntPtr WindowFromPoint(Point pt);
[DllImport("shell32.dll")]
public static extern int ShellExecute(IntPtr hwnd, StringBuilder lpszOp, StringBuilder lpszFile, StringBuilder lpszParams, StringBuilder lpszDir, int FsShowCmd);
[DllImport("user32.dll")]
private static extern bool EnumWindows(WNDENUMPROC lpEnumFunc, int lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, string lparam);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]//查找窗口
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[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);
private delegate bool WNDENUMPROC(IntPtr hWnd, int lParam);
//查找子控件
[DllImport("user32.dll", EntryPoint = "FindWindowEx", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
//发送消息
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, string lParam);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern IntPtr SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
//窗口置顶
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
//遍历子控件
[DllImport("user32.dll")]
public static extern int EnumChildWindows(IntPtr hWndParent, CallBack lpfn, int lParam);
//回调函数
public delegate bool CallBack(IntPtr hwnd, int lParam);
//根据窗体名称Form1,找到对应的句柄
IntPtr intPtr1 = FindWindowInfo("Form1");
if (intPtr1 != IntPtr.Zero)
{
//根据窗体句柄找到对应的控件信息
ControInfo[] xxxx = GetALLControls(intPtr1);
foreach (var item in xxxx)
{
//根据控件的句柄信息,判断哪个是文本输入框
IntPtr input = FindWindowEx(intPtr1, item.hWnd, null, null);
if (input == IntPtr.Zero)
{ //注入文字
SendMessage(item.hWnd, WM_SETTEXT, IntPtr.Zero, "我是测试");
//输入回车符
// SendKeys.Send("{ENTER}");可以用这个方式,但是这种方式受限光标在哪可以
SendMessage(item.hWnd, WM_CHAR, 0, VK_RETURN);
SendMessage(item.hWnd, WM_CHAR, (IntPtr)VK_RETURN, IntPtr.Zero);//Enter
}
}
}
}
private const int WM_KEYDOWN = 0X100;
private const int WM_KEYUP = 0X101;
private const int WM_SYSCHAR = 0X106;
private const int WM_SYSKEYUP = 0X105;
private const int WM_SYSKEYDOWN = 0X104;
private const int WM_CHAR = 0X102;
private const int VK_RETURN = 0X0d;
效果展示