业务场景
最近接到一个需求,向一个程序(简称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;
效果展示