7.1 模拟键盘输入
7.1.1 Introduction
此部分我们通过System.Windows.Forms.SendKeys类中的Send方法来模拟通过键盘输入数据到相应的控件中。
7.1.2 SendKeys Class Introduction(MSDN)
使用 SendKeys 将键击和组合键击发送到活动应用程序。此类无法实例化。若要发送一个键击给某个类并立即继续程序流,请使用 Send。若要等待键击启动的任何进程,请使用 SendWait。
每个键都由一个或多个字符表示。若要指定单个键盘字符,请使用该字符本身。例如,若要表示字母 A,请将字符串“A”传递给方法。若要表示多个字符,请将各个附加字符追加到它之前的字符的后面。若要表示字母 A、B 和 C,请将参数指定为“ABC”。
加号 (+)、插入符号 (^)、百分号 (%)、波浪号 (~) 以及圆括号 ( ) 对 SendKeys 具有特殊含义。若要指定这些字符中的某个字符,请将其放在大括号 ({}) 内。例如,若要指定加号,请使用“{+}”。若要指定大括号字符,请使用“{{}”和“{}}”。中括号 ([ ]) 对 SendKeys 没有特殊含义,但必须将它们放在大括号内。在其他应用程序中,中括号具有特殊含义,此含义可能会在发生动态数据交换 (DDE) 时起重要作用。
7.1.3 SendKeys Method(MSDN)
SendKeys有两个重要的方法:
1. Send方法:向活动应用程序发送击键。
2. SendWait方法:向活动应用程序发送给定的键,然后等待消息被处理。
有关SendKeys的参考: 英文站点:http://msdn.microsoft.com/en-us/library/8c6yea83(VS.85).aspx 中文站点:http://msdn.microsoft.com/zh-cn/library/system.windows.forms.sendkeys_methods(VS.80).aspx |
下面我们通过一个实例来演示通过SendKeys类来模拟键盘操作进行数据输入和快捷菜单的打开:
using System; using System.Text; using System.Diagnostics; using System.Threading; using System.Windows.Automation; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Forms; namespace UIATest { class Program { static void Main(string[] args) { Process process = Process.Start(@"E:\WorkBook\ATP\WpfApp\bin\Debug\WpfApp.exe"); int processId = process.Id; AutomationElement element = FindElementById(processId, "textBox1"); SendKeys sendkeys = new SendKeys(); sendkeys.Sendkeys(element, "Sending keys to input data"); Console.WriteLine(sendkeys.ToString()); sendkeys.Sendkeys(element, sendkeys.ContextMenu); Console.WriteLine(sendkeys.ToString()); Console.WriteLine("Test finised."); } /// <summary> /// Get the automation elemention of current form. /// </summary> /// <param name="processId">Process Id</param> /// <returns>Target element</returns> public static AutomationElement FindWindowByProcessId(int processId) { AutomationElement targetWindow = null; int count = 0; try { Process p = Process.GetProcessById(processId); targetWindow = AutomationElement.FromHandle(p.MainWindowHandle); return targetWindow; } catch (Exception ex) { count++; StringBuilder sb = new StringBuilder(); string message = sb.AppendLine(string.Format("Target window is not existing.try #{0}", count)).ToString(); if (count > 5) { throw new InvalidProgramException(message, ex); } else { return FindWindowByProcessId(processId); } } } /// <summary> /// Get the automation element by automation Id. /// </summary> /// <param name="windowName">Window name</param> /// <param name="automationId">Control automation Id</param> /// <returns>Automatin element searched by automation Id</returns> public static AutomationElement FindElementById(int processId, string automationId) { AutomationElement aeForm = FindWindowByProcessId(processId); AutomationElement tarFindElement = aeForm.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.AutomationIdProperty, automationId)); return tarFindElement; } } } |
我们自定义一个SendKeys类来对已有的SendKeys类进行优化,代码如下:
using System; using System.Windows.Automation; using System.Threading; using System.Text; using System.Windows.Forms; namespace UIATest { public class SendKeys { StringBuilder builder = new StringBuilder(); public string Alt = "%"; public string ContextMenu = "+{F10}"; public string Ctrl = "^"; public string Shift = "+"; public string Enter = "{Enter}"; public string Delete = "{Del}"; public string Save = "^S"; public string SaveAll = "^+S"; public string Copy = "^C"; public string Cut = "^X"; public string Paste = "^V"; public string Undo = "^Z"; public string Redo = "^Y"; public string Print = "^P"; public string Help = "{F1}"; public string New = "^N"; public string[] Keys{ get; set; } public void Sendkeys(AutomationElement element, string[] keys) { this.Keys = keys; try { element.SetFocus(); } catch (Exception exception) { throw new Exception("Cannot set focus to this element.", exception); } string myKeys = ""; foreach (string str2 in this.Keys) { myKeys = myKeys + str2; } Thread.Sleep(200); if ((this.ContainsUnescapedKey(myKeys, '^') || this.ContainsUnescapedKey(myKeys, '%')) || this.ContainsUnescapedKey(myKeys, '+')) { myKeys = myKeys.ToLower(); } System.Windows.Forms.SendKeys.SendWait(myKeys); Thread.Sleep(0x3e8); } public void Sendkeys(AutomationElement element, string myKeys) { this.Keys = new string[1]; this.Keys[0] = myKeys; try { element.SetFocus(); } catch (Exception exception) { throw new Exception("Cannot set focus to this element.", exception); } Thread.Sleep(200); if ((this.ContainsUnescapedKey(myKeys, '^') || this.ContainsUnescapedKey(myKeys, '%')) || this.ContainsUnescapedKey(myKeys, '+')) { myKeys = myKeys.ToLower(); } System.Windows.Forms.SendKeys.SendWait(myKeys); Thread.Sleep(0x3e8); } private bool ContainsUnescapedKey(string keys, char key) { for (int i = 0; i < keys.Length; i++) { if (keys[i] == key) { if ((i == 0) || (i == (keys.Length - 1))) { return true; } if ((keys[i - 1] != '{') || (keys[i + 1] != '}')) { return true; } } } return false; } private string KeysToString(string[] keys) { if (keys != null) { for (int i = 0; i < keys.Length; i++) { string str = keys[i]; if (str == null) { builder.Append(keys[i]); } int length = keys.Length - 1; switch (str) { case "^": builder.Append("Ctrl"); IsEquals(i, length, builder); break; case "+{F10}": builder.Append("Open Context Menu"); IsEquals(i, length, builder); break; case "%": builder.Append("Alt"); IsEquals(i, length, builder); break; case "+": builder.Append("Shift"); IsEquals(i, length, builder); break; case "^S": builder.Append("Save"); IsEquals(i, length, builder); break; case "^X": builder.Append("Cut"); IsEquals(i, length, builder); break; case "^C": builder.Append("Copy"); IsEquals(i, length, builder); break; case "^V": builder.Append("Paste"); IsEquals(i, length, builder); break; case "^+S": builder.Append("Save All"); IsEquals(i, length, builder); break; case "^P": builder.Append("Print"); IsEquals(i, length, builder); break; case "^Z": builder.Append("Undo"); IsEquals(i, length, builder); break; case "^Y": builder.Append("Redo"); IsEquals(i, length, builder); break; case "^N": builder.Append("New"); IsEquals(i, length, builder); break; default: builder.Append(keys[i]); IsEquals(i, length, builder); break; } } } return builder.ToString(); } void IsEquals(int i, int length, StringBuilder builder) { if(i<length) builder.Append("+"); } #region Public Method public override string ToString() { return string.Format("Sendkeys to input data or operator with keys = '{0}'", this.KeysToString(Keys)); } #endregion } } |
如下代码为对应的WPF窗体XAML代码:
<Window x:Class="WpfApp.SendKeysInputData" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="SendKeysInputData" Height="250" Width="367"> <Grid> <TextBox Margin="28,80,41,101" Name="textBox1"> <TextBox.ContextMenu> <ContextMenu> <MenuItem Header="Open" /> <MenuItem Header="Save" /> <MenuItem Header="New" /> <MenuItem Header="Save As" /> <MenuItem Header="Load" /> <MenuItem Header="ReLoad" /> </ContextMenu> </TextBox.ContextMenu> </TextBox> </Grid> </Window> |
7.1.4 Summary
本小节首先简单介绍了如何在.NET通过调用Win32 API来模拟键盘的操作,进而通过实例演示了模拟鼠标与模拟键盘在基于UI Automation的自动化测试中应用。