admin管理员组文章数量:1588984
近期玩了下 UIAutomation。C# 中有 UI Automation 库,C++可以看msdn的 Accessibility。这两个东西网上能找到的东西太少了,只能自己看微软的官方文档。我把我的一些代码段贴到下面,希望能帮助需要的人。Python有个库 UIAutomation,就是封装微软提供的 UIAutomation,网上有一些 资料。但是这个库的作者说没有准备文档,所以需要的人自己看Demo去猜函数该怎么用吧。我用的时候结合了另一个库 pyautogui,用于操作键盘、鼠标。
Python 的 UI Automation 这个库怎么用可以直接看 uiautomation.py 这个文件,其中 class Control() 这个在5156行。在寻找控件的时候可先用命令行工具 automation.py -t3 去查找,具体参考 这个网页;关于找到元素的代码参考 这个。在遇到问题时可以参考 C# 的代码。比如从 EditControl 获取内容:
var clickPattern = (TextPattern)textedit.GetCurrentPattern(TextPattern.Pattern);
Console.WriteLine(clickPattern.DocumentRange.GetText(100));
Console.WriteLine(clickPattern.DocumentRange.GetText(100));
相应的 python 代码:
EditControl.GetPattern(PatternId.TextPattern).DocumentRange.GetText(100)
传统的Win32程序有句柄这个东西,每个控件的句柄很容易获取。WPF 程序的句柄就一个,是主窗口的,里边的控件是框架渲染的,所以 spy++ 无能为力了。UIAutomation是向 WPF 窗体发消息,如果窗口不处理这个消息,那也是获取不到控件的,比如QQ轻聊版。处理消息的是 LresultFromObject 函数。引用 大牛 的话:
UIAutomation的工作原理是: 当你用UIAutomation操作程序时,UIAutomation会给程序发送WM_GETOBJECT消息, 如果程序处理WM_GETOBJECT消息,实现UI Automation Provider,并调用函数 UiaReturnRawElementProvider(HWND hwnd,WPARAM wparam,LPARAM lparam,IRawElementProviderSimple *el), 此程序就支持UIAutomation。
IRawElementProviderSimple就是UI Automation Provider,包含了控件的各种信息,如Name,ClassName,ContorlType,坐标... UIAutomation根据程序返回的IRawElementProviderSimple,就能遍历程序的控件,得到控件各种属性,进行自动化操作。
所以如果你发现UIAutomation不能识别一些程序内的控件或部分不支持,这并不是UIAutomation的问题, 是程序作者没有处理WM_GETOBJECT或没有实现UIAutomation Provider,或者故意不想支持UIAutomation。 很多DirectUI程序都没有实现UIAutomation Provider,所以不支持自动化,要想支持自动化,必须程序作者修改源码支持。
和 spy++ 类似的有个 snoop,在 GitHub 上开源的,可以看 WPF 窗体的控件树。自动化测试的商业软件有 Ranorex,可以试用30天。他录制的过程生成了一个解决方案,这个解决方案可以直接在 VS 中打开进行更改。还有些 FlaUI,UISpy.exe,QAliber,White.NUnit IL ,Inspector,ildasm,ilspy 我就没尝试了。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Diagnostics;
using System.Threading;
using System.Windows.Automation.Provider;
using System.Windows.Automation.Text;
using System.Windows.Automation;
using System.Windows.Forms;
using System.Runtime.InteropServices; // DllImport() 需要它。
namespace UITest
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName,string lpWindowName);
// Activate an application window.
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
public MainWindow()
{
InitializeComponent();
try
{
Console.WriteLine("\nBegin WinForm UIAutomation test run\n");
// launch Form1 application
// get refernce to main Form control
// get references to user controls
// manipulate application
// check resulting state and determine pass/fail
Console.WriteLine("\nBegin WinForm UIAutomation test run\n");
Console.WriteLine("Launching WinFormTest application");
//启动被测试的程序
Process p = Process.Start(@"D:\Program Files (x86)\{exe文件路径}");
//自动化根元素.桌面的根。
AutomationElement aeDeskTop = AutomationElement.RootElement;
Thread.Sleep(2000);
// AutomationElement aeForm = AutomationElement.FromHandle(p.MainWindowHandle);
AutomationElement aeForm = null;
//获得对主窗体对象的引用,该对象实际上就是 Form1 应用程序(方法一)
//if (null == aeForm)
//{
// Console.WriteLine("Can not find the WinFormTest from.");
//}
//获得对主窗体对象的引用,该对象实际上就是 Form1 应用程序(方法二)
//有些exe启动另外一个exe自己就退出了,所以要自己再找一遍。
int numWaits = 0;
do
{
Console.WriteLine("Looking for WinFormTest……");
//查找第一个自动化元素
aeForm = aeDeskTop.FindFirst(TreeScope.Children, new PropertyCondition(
AutomationElement.NameProperty, "{窗口名称}"));
++numWaits;
Thread.Sleep(100);
} while (null == aeForm && numWaits < 50);
if (null == aeForm)
throw new NullReferenceException("Failed to find WinFormTest.");
else
Console.WriteLine("Found it!");
Console.WriteLine("Finding all user controls");
//找到第一次出现的Button控件
// AutomationElement aeButton = aeForm.FindFirst(TreeScope.Children,
// new PropertyCondition(AutomationElement.NameProperty, "Button"));
// 根据Button控件Content属性精确查找。
//找到所有的TextBox控件
//AutomationElementCollection aeAllTextBoxes = aeForm.FindAll(TreeScope.Children,
// new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
// 控件初始化的顺序是先初始化后添加到控件
// this.Controls.Add(this.textBox3);
// this.Controls.Add(this.textBox2);
// this.Controls.Add(this.textBox1);
//AutomationElement aeTextBox1 = aeAllTextBoxes[2];
//AutomationElement aeTextBox2 = aeAllTextBoxes[0];
//AutomationElement aeTextBox3 = aeAllTextBoxes[1];
// 这个顺序与你编程的时候往面板上拖控件的顺序有关。
Thread.Sleep(3000);
AutomationElementCollection aeRadioButtons = aeForm.FindAll(TreeScope.Descendants,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.RadioButton));
Console.WriteLine(aeRadioButtons[10].Current.Name); // 这一句必须要有,否则aeRadioButtons就是个空的,奇怪!
IntPtr hWnd =FindWindow(null,"{窗口名}");
//for (int i = 0; i < 100; i++) {
// Console.WriteLine(aeWhats[i].GetCurrentPropertyValue(AutomationElement.CultureProperty) as string);
// Console.WriteLine(i.ToString()); // 只发现了一个。
//}
for (int i = 0; i < aeRadioButtons.Count; i++)
{
Console.WriteLine("按钮名字:");
Console.WriteLine(aeRadioButtons[i].Current.Name);
if (aeRadioButtons[i].Current.Name == "{按钮名字}")
{
Console.WriteLine("找到RadioButton");
// 这里的RadioButton不是用来选的,是用来点击的。文档说RadioButon仅支持SelectionItemPattern,不支持点击。
// SelectionItemPattern selectionItemPattern = aeRadioButtons[i].GetCurrentPattern(SelectionItemPattern.Pattern) as SelectionItemPattern;
// selectionItemPattern.Select();
Thread.Sleep(100);
SetForegroundWindow(hWnd);
aeRadioButtons[i].SetFocus();
Console.WriteLine("已设置焦点");
// 这个不行,原因 https://stackoverflow/questions/2958561/when-using-sendkeys-invalidoperationexception-undo-operation-encountered
//System.Windows.Forms.SendKeys.Send("{ENTER}");
// System.Windows.Forms.Control.Invoke(Delegate method);
Console.WriteLine("Select后");
}
}
// 此处需要刷新控件树,界面已刷新。
//refresh TreeScope
// 窗口刷新以后应该按新窗口对待。本来最开始的启动程序后获得的句柄也没什么用。
Console.WriteLine("开始转换界面");
Thread.Sleep(30*1000);
AutomationElementCollection aeTextBlocks = aeForm.FindAll(TreeScope.Descendants,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Document));
Console.WriteLine("尝试输出一个Label");
Console.WriteLine(aeTextBlocks[0].Current.LabeledBy); // 这一句必须要有,否则aeRadioButtons就是个空的,奇怪!
for (int i = 0; i < aeTextBlocks.Count; i++)
{
Console.WriteLine("文本框内容:");
Console.WriteLine(aeRadioButtons[i].Current.Name);
}
// TreeWalker walker = new TreeWalker(condition3);
//AutomationElement elementNode = walker.GetFirstChild(aeWhats[0]);
//AutomationElementCollection aeRadioButtons = aeForm.FindAll(TreeScope.Children,
// new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.RadioButton));
//for(int i=0;i<100;i++)
// Console.WriteLine(aeRadioButtons[0].GetCurrentPropertyValue(AutomationElement.CultureProperty) as string);
//-----------------------
//Console.WriteLine("Settiing input to '30'");
通过ValuePattern设置TextBox1的值
//ValuePattern vpTextBox1 = (ValuePattern)aeTextBox1.GetCurrentPattern(ValuePattern.Pattern);
//vpTextBox1.SetValue("30");
//Console.WriteLine("Settiing input to '50'");
通过ValuePattern设置TextBox2的值
//ValuePattern vpTextBox2 = (ValuePattern)aeTextBox2.GetCurrentPattern(ValuePattern.Pattern);
//vpTextBox2.SetValue("50");
//Thread.Sleep(1500);
//Console.WriteLine("Clickinig on button1 Button.");
通过InvokePattern模拟点击按钮
//InvokePattern ipClickButton1 = (InvokePattern)aeButton.GetCurrentPattern(InvokePattern.Pattern);
//ipClickButton1.Invoke();
//Thread.Sleep(1500);
验证计算的结果与预期的结果是否相符合
//Console.WriteLine("Checking textBox3 for '80'");
//TextPattern tpTextBox3 = (TextPattern)aeTextBox3.GetCurrentPattern(TextPattern.Pattern);
//string result = tpTextBox3.DocumentRange.GetText(-1);//获取textbox3中的值
// //获取textbox3中的值
// //string result = (string)aeTextBox2.GetCurrentPropertyValue(ValuePattern.ValueProperty);
//if ("80" == result)
//{
// Console.WriteLine("Found it.");
// Console.WriteLine("TTest scenario: *PASS*");
//}
//else
//{
// Console.WriteLine("Did not find it.");
// Console.WriteLine("Test scenario: *FAIL*");
//}
//Console.WriteLine("Close application in 5 seconds.");
//Thread.Sleep(5000);
实现关闭被测试程序
//WindowPattern wpCloseForm = (WindowPattern)aeForm.GetCurrentPattern(WindowPattern.Pattern);
//wpCloseForm.Close();
//Console.WriteLine("\nEnd test run\n");
}
catch (Exception ex)
{
Console.WriteLine("Fatal error: " + ex.Message);
}
}
// 遍历控件树,把类型名放入TreeNode(Windows.Forms中的类)里
private void WalkEnabledElements(AutomationElement rootElement, TreeNode treeNode)
{
System.Windows.Automation.Condition condition1 = new PropertyCondition(AutomationElement.IsControlElementProperty, true);
System.Windows.Automation.Condition condition2 = new PropertyCondition(AutomationElement.IsEnabledProperty, true);
System.Windows.Automation.Condition condition3 = new PropertyCondition(AutomationElement.IsContentElementProperty, true);
TreeWalker walker = new TreeWalker(new AndCondition(condition1, condition2));
AutomationElement elementNode = walker.GetFirstChild(rootElement);
while (elementNode != null)
{
TreeNode childTreeNode = treeNode.Nodes.Add(elementNode.Current.ControlType.LocalizedControlType);
WalkEnabledElements(elementNode, childTreeNode);
elementNode = walker.GetNextSibling(elementNode);
}
}
// 操作ListItem所需的Pattern
AutomationElement GetListItemParent(AutomationElement listItem)
{
if (listItem == null) throw new ArgumentException();
SelectionItemPattern pattern = listItem.GetCurrentPattern(SelectionItemPattern.Pattern) as SelectionItemPattern;
if (pattern == null)
{
return null;
}
else
{
SelectionItemPattern.SelectionItemPatternInformation properties = pattern.Current;
return properties.SelectionContainer;
}
}
}
}
版权声明:本文标题:自动操作软件 获取软件按钮内容 UIAutomation 软件自动化测试(我的一点补充) 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dianzi/1728049514a1143513.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论