代码改变世界

微软平台UI自动化(UIA)经验集

2010-05-20 20:30  lixiong  阅读(5273)  评论(3编辑  收藏  举报

根据我UIA自动化测试的经验, 总结了下面代码集. 在这个代码集中, 包含了:

 

1. 一个WPF的窗体程序

2. 一个WinForm的窗体, 这个窗体作为Model Dialog被WPF主程序打开

3. 针对这个WPF和WinForm的测试代码例子

4. 针对Win7 Calc.exe的测试代码例子

5. 一个简单的TestEngine

 

这个代码集的作用是:

 

1. 演示UIA中基本的概念, 比如AutomationID, AutomatonName, InvokePattern等的调用

2. 演示如何处理UI自动化的timing issue.

3. 演示简单WaitForReady的实现方法

4. 演示Click和Invoke的差别

5. 演示一个简单的UIA Engine

6. 演示如何通过AutomationPeer来给自绘画图案实现Invoke Pattern

7. 演示如何对WinForm实现Server side provider

8. 演示如何对WPF的databinding item设定AutomationID

 

WPF主窗口代码:

 

代码
public partial class Window1 : Window
    {
        
private StackPanel pane;
        
private TextBlock timeBlock;
        
private Button buttonOpenNewWindow;
        
private ListBox listboxStringBinding;
        
private ListBox listboxManual;
        
private SelfDrawControl selfControl;
        
private DispatcherTimer tmr=new DispatcherTimer();
        
private DispatcherTimer deplyedExecution=new DispatcherTimer();
        
private Button kickoffFlashWindow;
        
private Button startWinformHoster;
        
private Button buttonNoAutomationID;
        
private Button busyButton;

        
private int busyCount = 0;

        
public Window1()
        {
            InitializeComponent();
            
this.Name = "Window1";
            CreateControls();
            
        }

        
void CreateControls()
        {
            pane 
= new StackPanel();
            pane.Name 
= "StackPane";
            
this.Content = pane;

            timeBlock 
= new TextBlock();
            timeBlock.Name 
= "TimeBlock";
            pane.Children.Add(timeBlock);

            buttonOpenNewWindow 
= new Button();
            buttonOpenNewWindow.Name 
= "ButtonOpenNewWindow";
            buttonOpenNewWindow.Content 
= "Open New Window";
            pane.Children.Add(buttonOpenNewWindow);

            
//直接binding的Listbox子元素没有AutomationID
            listboxStringBinding = new ListBox();
            listboxStringBinding.Name 
= "ListBox_Item_WithoutAutomationID";
            listboxStringBinding.DataContext 
= new string[] { "1""2""3" };
            Binding bind 
= new Binding();
            bind.Source 
= new string[] { "1""2""3" };             
            listboxStringBinding.SetBinding(ListBox.ItemsSourceProperty, bind);
            pane.Children.Add(listboxStringBinding);
           

            selfControl 
= new SelfDrawControl();
            selfControl.Name 
= "SelfDrawControl";
            pane.Children.Add(selfControl);

            listboxManual 
= new ListBox();
            listboxManual.Name 
= "ListBox_Item_ManualBindAutomationID";
            
for(int i=0;i<5;i++)
            {
                
//手动添加的的Listbox子元素可以通过下面的方法指定AutomationID,或者直接指定Name
                ListBoxItem item=new ListBoxItem();
                item.SetValue(System.Windows.Automation.AutomationProperties.AutomationIdProperty, 
"ManualItem" + i.ToString());
                item.Content 
=i;
                listboxManual.Items.Add(item);
            }
            pane.Children.Add(listboxManual);

            kickoffFlashWindow 
= new Button();
            kickoffFlashWindow.Name 
= "ButtonOpenFlashWindow";
            kickoffFlashWindow.Content 
= "Kick off Flash Window";
            pane.Children.Add(kickoffFlashWindow);
            kickoffFlashWindow.Click 
+= new RoutedEventHandler(kickoffFlashWindow_Click);

            startWinformHoster 
= new Button();
            startWinformHoster.Name 
= "ButtonOpenWinForm";
            startWinformHoster.Content 
= "Start Winform Hoster";
            pane.Children.Add(startWinformHoster);
            
//startWinformHoster.Click += delegate{(new WinFormControlHoster()).ShowDialog();};
            startWinformHoster.Click += new RoutedEventHandler(startWinformHoster_Click);

            tmr.Interval 
= new TimeSpan(0,0,0,0,500);
            tmr.Tick 
+= delegate { this.timeBlock.Text = DateTime.Now.ToString("U"); };
            tmr.IsEnabled 
= true;
            buttonOpenNewWindow.Click 
+= new RoutedEventHandler(buttonOpenNewWindow_Click);

            buttonNoAutomationID 
= new Button();
            buttonNoAutomationID.Content 
= "Button Wihtout AutomationID";
            pane.Children.Add(buttonNoAutomationID);

            busyButton 
= new Button();
            busyButton.Name 
= "BusyButton";
            busyButton.Content 
= busyCount.ToString();
            pane.Children.Add(busyButton);
            busyButton.Click 
+= new RoutedEventHandler(busyButton_Click);

            deplyedExecution.Interval 
= new TimeSpan(005);
            deplyedExecution.Tick 
+= new EventHandler(deplyedExecution_Tick);
            deplyedExecution.IsEnabled 
= false;
        }

        
void busyButton_Click(object sender, RoutedEventArgs e)
        {
            busyCount
++;
            busyButton.Content 
= busyCount.ToString();
            
for (int i = 1; i < 10; i++)
            {
                System.Threading.Thread.Sleep(
150);
            }
        }

        
void buttonOpenNewWindow_Click(object sender, RoutedEventArgs e)
        {
            BindingWithAutomationID bwid 
= new BindingWithAutomationID();
            bwid.ShowDialog();
        }

        
void kickoffFlashWindow_Click(object sender, RoutedEventArgs e)
        { 
            deplyedExecution.IsEnabled 
= true;
        }

        
void deplyedExecution_Tick(object sender, EventArgs e)
        {
            FlashWindow fw 
= new FlashWindow(new TimeSpan(0,0,3));           
            fw.Show();
            deplyedExecution.IsEnabled 
= false;            
        }

        
void startWinformHoster_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Forms.Form fm 
= new MyForm();
            fm.ShowDialog();
        } 
    }

 

 

WPF自绘窗口的代码, 以及对应AutomationPeer的实现:

 

代码
public partial class SelfDrawControl : UserControl
    {
        
public SelfDrawControl()
        {
            InitializeComponent();
        }

        
protected override void OnRender(DrawingContext dc)
        {
            dc.DrawRectangle(Brushes.Blue, 
new Pen(Brushes.Blue, 10), new Rect(new Point(00), new Point(RenderSize.Width / 2, RenderSize.Height)));
            dc.DrawRectangle(Brushes.Black, 
new Pen(Brushes.Black, 10), new Rect(new Point(RenderSize.Width / 20), new Point(RenderSize.Width, RenderSize.Height)));
        
        }

        
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            Point point 
= e.GetPosition(this);
            
if (point.X > 0 && point.X < RenderSize.Width / 2)
            {
                DoClick(
"blue");
            }
            
else
            {
                DoClick(
"black");
            }
            
base.OnMouseLeftButtonDown(e);
        }

        
internal void DoClick(string color)
        {
            MessageBox.Show(color);
        }

        
protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
        {
            var peer 
= new SelfDrawControlAutomationPeer(this);
            peer.InvalidatePeer();
            
return peer;
        }       
    }

    
public class SelfDrawControlAutomationPeer :UserControlAutomationPeer
    {
        SelfDrawControl target;
        List
<AutomationPeer> children = null;
        
public SelfDrawControlAutomationPeer(SelfDrawControl target):base(target)
        {
            
this.target = target;
        }

        
protected override AutomationControlType GetAutomationControlTypeCore()
        {
            
return AutomationControlType.Window;
        }

        
protected override string GetClassNameCore()
        {
            
return target.GetType().ToString();
        }

        
/*
         * 特别注意, 生成ChildrenCore的时候务必维护父子关系
         * UI Testclient 可能在不同child parent之间遍历
         * 务必保证childpeer.Parent = parent.Children[n]
         * 否则会带来各种意外,这是在实现自定义Peer时候的最大陷阱
        
*/ 

        
protected override List<AutomationPeer> GetChildrenCore()
        {
            
//return null;

            
if (children == null)
            {
                children 
= new List<AutomationPeer>();
                SelfDrawControlElementAutomationPeer bluepeer 
= new SelfDrawControlElementAutomationPeer(target, this"blue");
                SelfDrawControlElementAutomationPeer redpeer 
= new SelfDrawControlElementAutomationPeer(target, this"red");
                children.Add(bluepeer);
                children.Add(redpeer);
            }

            
return children;
        }

    }

    
public class SelfDrawControlElementAutomationPeer : AutomationPeer, IInvokeProvider, IValueProvider
    {
        
private string color;
        SelfDrawControl target;
        SelfDrawControlAutomationPeer parentPeer;

        
public SelfDrawControlElementAutomationPeer(SelfDrawControl target, SelfDrawControlAutomationPeer parentPeer, string color)             
        {
            
this.color = color;
            
this.target = target;
            
this.parentPeer = parentPeer;

            var o
=this.GetParent();
        }

        
public void Invoke()
        {
            target.DoClick(color);
        }

        
public bool IsReadOnly { get { return true; } }
        
public string Value { get { return color; } }
        
public void SetValue(string value) { }

        
protected override string GetAcceleratorKeyCore()
        {
            
return string.Empty;
        }
       
   
        
protected override string GetAccessKeyCore()
        {
            
return string.Empty;
        }
      
        
protected override AutomationControlType GetAutomationControlTypeCore()
        {
            
return AutomationControlType.Window;
        }
      
        
protected override string GetAutomationIdCore()
        {           
            
return target.Name+"-"+color;
        }
    
        
protected override Rect GetBoundingRectangleCore()
        {

            Rect parentRect
=parentPeer.GetBoundingRectangle();

            
if(color=="blue")
            {
                
return new Rect(new Point(parentRect.X, parentRect.Y), new Point(parentRect.X + target.RenderSize.Width / 2, parentRect.Y+target.RenderSize.Height));
            }
            
if(color=="red")
            {
                
return new Rect(new Point(parentRect.X + target.RenderSize.Width / 2, parentRect.Y), new Point(parentRect.X + target.RenderSize.Width, parentRect.Y + target.RenderSize.Height));
            }
            
return Rect.Empty;
        }
     
        
protected override List<AutomationPeer> GetChildrenCore()
        {
            
return null;
        }
   
        
protected override string GetClassNameCore()
        {
            
return "SelfDrawControlInnerGraphic";
        }
      
        
protected override Point GetClickablePointCore()
        {
            
return new Point(0,0);
        }
  
        
protected override string GetHelpTextCore()
        {
            
return "This is my HelpText";
        }
      
        
protected override string GetItemStatusCore()
        {
            
return "Status is active";
        }
    
        
protected override string GetItemTypeCore()
        {
            
return "This is my item type";
        }
     
        
protected override AutomationPeer GetLabeledByCore()
        {
            
return this;
        }
      
        
protected override string GetNameCore()
        {
            
return "Name is :"+color;
        }
       
        
protected override AutomationOrientation GetOrientationCore()
        {
            
return AutomationOrientation.None;
        }
     
        
public override object GetPattern(PatternInterface patternInterface)
        {
            
if(patternInterface== PatternInterface.Invoke || patternInterface== PatternInterface.Value)
            {
                
return this;
            }
            
return null;
        }
     
        
protected override bool HasKeyboardFocusCore()
        {
            
return false;
        }
       
        
protected override bool IsContentElementCore()
        {
            
return true;
        }
     
        
protected override bool IsControlElementCore()
        {
            
return true;
        }
  
        
protected override bool IsEnabledCore()
        {
            
return true;
        }

        
protected override bool IsKeyboardFocusableCore()
        {
            
return false;
        }
   
        
protected override bool IsOffscreenCore()
        {
            
return false;
        }
      
        
protected override bool IsPasswordCore()
        {
            
return false;
        }
    
        
protected override bool IsRequiredForFormCore()
        {
            
return false;
        }
       
        
protected override void SetFocusCore()
        {
        }
    }

 

 

BindingWindow的XAML和代码:

<ListBox Name="InnerListbox" >
        <ListBox.Resources>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="AutomationProperties.AutomationId"  Value="{Binding  RelativeSource={x:Static RelativeSource.Self}, Path=Content}" ></Setter>
            </Style>
        </ListBox.Resources>       
    </ListBox>

 

代码
public partial class BindingWithAutomationID : Window
    {
        
public BindingWithAutomationID()
        {
            InitializeComponent();
            
this.Name = "StyleBindingWindow";

            
//可以使用Style的Setter来给binding的子元素增加AutomationID
            this.InnerListbox.DataContext = new string[] { "1""2""3" };
            Binding bind 
= new Binding();
            bind.Source 
= new string[] { "1""2""3" };
            
this.InnerListbox.SetBinding(ListBox.ItemsSourceProperty, bind);            
        }

    }

 

 

 

WinForm的代码及其Server side provider实现:

 

代码
[ComVisible(true)]
    
public class MyForm : System.Windows.Forms.Form, IRawElementProviderSimple
    {
        
private ValuePattern vp = new ValuePattern();
        
private Timer timer = new Timer();
        
private string curentNameProperty = string.Empty;

        
public MyForm()
        {
            timer.Interval 
= 500;
            timer.Tick 
+= new EventHandler(timer_Tick);
            timer.Enabled 
= true;

            
this.Name = "WinFormWindow";
            
this.Text = "ServerUIAFormDemo";

            Button btn 
= new Button();
            btn.Text 
= "This is a WinForm Button";
            btn.Name 
= "button1";
            
this.Controls.Add(btn);

            curentNameProperty 
= string.Format("{0} {1}"this.Name, DateTime.Now.ToLongTimeString());
        }

        
void timer_Tick(object sender, EventArgs e)
        {
            
string newNameProperty=string.Format("{0} {1}"this.Name, DateTime.Now.ToLongTimeString());
            AutomationInteropProvider.RaiseAutomationPropertyChangedEvent(
thisnew AutomationPropertyChangedEventArgs(AutomationElement.NameProperty, curentNameProperty, newNameProperty));
            curentNameProperty 
= newNameProperty;
        }

        
protected override void WndProc(ref Message m)
        {

            
const int WM_GETOBJECT = 0x003D;
            
if ((m.Msg == WM_GETOBJECT) && (m.LParam.ToInt32() ==
                AutomationInteropProvider.RootObjectId))
            {

                m.Result 
= AutomationInteropProvider.ReturnRawElementProvider(
                        
this.Handle, m.WParam, m.LParam,
                        (IRawElementProviderSimple)
this);
                
return;

            }
            
base.WndProc(ref m);
        } 

        
public Object GetPatternProvider(int patternId)
        {

            
if (patternId == ValuePatternIdentifiers.Pattern.Id)
            {
                
//Create and return ValuePattern object
                return vp;
            }
            
else
            {
                
return null;
            }
        }

        
//This function handles all the UIA Property reqeust
        public Object GetPropertyValue(int propertyId)
        {

            
if (propertyId == AutomationElementIdentifiers.NameProperty.Id)
            {
                
return curentNameProperty;
            }

            
else if (propertyId == AutomationElementIdentifiers.NativeWindowHandleProperty.Id)
            {
                
return this.Handle;
            }

            
else if (propertyId == AutomationElementIdentifiers.AutomationIdProperty.Id)
            {
                
return this.Name;
            }

            
else if (propertyId == AutomationElementIdentifiers.ClassNameProperty.Id)
            {
                
return "RootButtonControlClass";
            }

            
else if (propertyId == AutomationElementIdentifiers.ControlTypeProperty.Id)
            {
                
return ControlType.Window.Id;
            }

            
else if (propertyId == AutomationElementIdentifiers.IsContentElementProperty.Id)
            {
                
return false;
            }

            
else if (propertyId == AutomationElementIdentifiers.IsControlElementProperty.Id)
            {
                
return true;
            }
            
else
            {
                
return AutomationElement.NotSupported;
            }

        }

        
public IRawElementProviderSimple HostRawElementProvider
        {
            
get
            {
                
return AutomationInteropProvider.HostProviderFromHandle(this.Handle);
            }
        }

        
public ProviderOptions ProviderOptions
        {
            
get
            {
                
//Indicate this is server side implementation
                return ProviderOptions.ServerSideProvider;
            }
        }
    }

    [ComVisible(
true)]
    
public class ValuePattern : IValueProvider
    {
        
public bool IsReadOnly { get { return true; } }
        
public string Value
        {
            
get
            {
                
//Return current time as value pattern’s value
                return DateTime.Now.ToLongTimeString();
            }
        }
        
public void SetValue(string value) { return; }
    }

 

 

下面是测试程序:

 

测试演示1: 演示UIA API里面的cached property:

 

代码
class DemoCachedProperty
    {
        
public static void Test()
        {
            CacheRequest cacheRequest 
= new CacheRequest();            
            cacheRequest.TreeScope 
= TreeScope.Element;
            cacheRequest.Add(AutomationElement.NameProperty);

            
using (cacheRequest.Activate())
            {
                AutomationElement wpfRoot 
= AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
                AutomationElement textBlock1 
= wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "TimeBlock"));
                AutomationElement textBlock2 
= wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "TimeBlock"));
                
for (int i = 0; i < 100; i++)
                {
                    
string cachedName = textBlock1.Cached.Name;
                    
string uncachedName = textBlock2.Current.Name;
                    Console.WriteLine(
"=================");
                    Console.WriteLine(
"Cached Name is {0}", cachedName);
                    Console.WriteLine(
"UnCached Name is {0}", uncachedName);
                    System.Threading.Thread.Sleep(
300);
                }
            }

            Console.WriteLine(
"Test finishes...................");
        }
    }

 

 

测试演示2: timing issue导致的问题和三种应对方法: Sleep/Polling/Event:

 

代码
class DemoDelayedWindow
    {
        
static DateTime eventStartTime;

        
public static void NoUISyncLeadToErrorDemo()
        {
            CacheRequest cacheRequest 
= new CacheRequest();
            cacheRequest.TreeScope 
= TreeScope.Element;
            cacheRequest.Add(AutomationElement.NameProperty);

            
using (cacheRequest.Activate())
            {
                AutomationElement wpfRoot 
= AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
                AutomationElement btnOpenNewWindow 
= wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "ButtonOpenFlashWindow"));
                InvokePattern invokPtn 
= (InvokePattern)btnOpenNewWindow.GetCurrentPattern(InvokePattern.Pattern);
                invokPtn.Invoke();
                Console.WriteLine(
"Button Clicked...");
                AutomationElement newWindow 
= AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));
                Console.WriteLine(newWindow.Current.Name);
            }

            Console.WriteLine(
"Test finishes...................");
        }

        
public static void SimpleSleepSyncDemo()
        {
            DateTime startTime 
= DateTime.Now;

            CacheRequest cacheRequest 
= new CacheRequest();
            cacheRequest.TreeScope 
= TreeScope.Element;
            cacheRequest.Add(AutomationElement.NameProperty);

            
using (cacheRequest.Activate())
            {
                AutomationElement wpfRoot 
= AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
                AutomationElement btnOpenNewWindow 
= wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "ButtonOpenFlashWindow"));
                InvokePattern invokPtn 
= (InvokePattern)btnOpenNewWindow.GetCurrentPattern(InvokePattern.Pattern);
                invokPtn.Invoke();
                Console.WriteLine(
"Button Clicked...");
                Console.WriteLine(
"Sleeping 6 seconds to wait...");
                System.Threading.Thread.Sleep(
1000 * 6);
                AutomationElement newWindow 
= AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));
                Console.WriteLine(newWindow.Current.Name);
            }

            Console.WriteLine(
"Test finishes...................");
            Console.WriteLine(
"Test cost {0} seconds", (DateTime.Now - startTime).TotalSeconds);
        }

        
public static void PollingSyncDemo()
        {
            DateTime startTime 
= DateTime.Now;
            CacheRequest cacheRequest 
= new CacheRequest();
            cacheRequest.TreeScope 
= TreeScope.Element;
            cacheRequest.Add(AutomationElement.NameProperty);

            
using (cacheRequest.Activate())
            {
                AutomationElement wpfRoot 
= AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
                AutomationElement btnOpenNewWindow 
= wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "ButtonOpenFlashWindow"));
                InvokePattern invokPtn 
= (InvokePattern)btnOpenNewWindow.GetCurrentPattern(InvokePattern.Pattern);
                invokPtn.Invoke();
                Console.WriteLine(
"Button Clicked...");

                
int timeout = 10 * 1000;
                
while (true)
                {
                    AutomationElement newWindow 
= AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));
                    
if (newWindow == null)
                    {
                        Console.WriteLine(
"Cannot find expected window. Sleep for a short time");
                        System.Threading.Thread.Sleep(
500);
                        timeout 
-= 300;
                        
if (timeout <= 0)
                        {
                            
throw new TimeoutException("Window probing process times out after 10 seconds.");
                        }
                    }
                    
else
                    {
                        Console.WriteLine(newWindow.Current.Name);
                        
break;
                    }
                }

            }

            Console.WriteLine(
"Test finishes...................");
            Console.WriteLine(
"Test cost {0} seconds", (DateTime.Now - startTime).TotalSeconds);
        }

        
public static void EventSyncDemo()
        {
            eventStartTime 
= DateTime.Now;
            CacheRequest cacheRequest 
= new CacheRequest();
            cacheRequest.TreeScope 
= TreeScope.Element;
            cacheRequest.Add(AutomationElement.NameProperty);

            
using (cacheRequest.Activate())
            {
                AutomationElement wpfRoot 
= AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
                AutomationElement btnOpenNewWindow 
= wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "ButtonOpenFlashWindow"));
                InvokePattern invokPtn 
= (InvokePattern)btnOpenNewWindow.GetCurrentPattern(InvokePattern.Pattern);
                Console.WriteLine(
"Register Event");
                AutomationEventHandler eventHandler 
= new AutomationEventHandler(OnWindowOpenOrClose);
                Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, eventHandler);
                Automation.AddAutomationEventHandler(WindowPattern.WindowClosedEvent, AutomationElement.RootElement, TreeScope.Subtree, eventHandler);

                invokPtn.Invoke();
                Console.WriteLine(
"Button Clicked and Wait...");
                Console.ReadLine();
            }
        }

        
static void OnWindowOpenOrClose(object src, AutomationEventArgs e)
        {
            Console.WriteLine(
"OnWindowOpenOrClose event triggers");
            
if (e.EventId != WindowPattern.WindowOpenedEvent)
            {
                Console.WriteLine(
"It is NOT WindowOpenedEvent.Ignore");
                
return;
            }

            AutomationElement sourceElement;
            
try
            {
                sourceElement 
= src as AutomationElement;
                
if (sourceElement.Current.AutomationId == "FlashWindow")
                {
                    Console.WriteLine(sourceElement.Current.Name);
                }
            }

            
catch (ElementNotAvailableException)
            {
                
return;
            }

            Console.WriteLine(
"Test finishes...................");
            Console.WriteLine(
"Test cost {0} seconds", (DateTime.Now - eventStartTime).TotalSeconds);
        }
}

 

 

测试演示3: 通过Waiter Pattern来简化timing issue的处理:

 

代码
class SimpleWaiter
    {
        
private AutomationEvent _eventId;
        
private AutomationElement _element;
        
private TreeScope _scope;
        
private System.Threading.AutoResetEvent _event;
        
private Condition _condition;
        
private AutomationEventHandler _eventHandler;

        
public SimpleWaiter(AutomationEvent eventId, AutomationElement element, TreeScope scope, Condition condition)
        {
            
this._eventId = eventId;
            
this._element = element;
            
this._scope = scope;
            
this._condition = condition;
            _event 
= new System.Threading.AutoResetEvent(false);
            _eventHandler 
= new AutomationEventHandler(Handler);
            Automation.AddAutomationEventHandler(_eventId, _element, _scope, _eventHandler);
        }

        
void Handler(object src, AutomationEventArgs e)
        {            
            AutomationElement sourceElement;
            sourceElement 
= src as AutomationElement;  
            var finditem 
= sourceElement.FindFirst(TreeScope.Element, _condition);
            
if (finditem != null && finditem.Equals(sourceElement))
            {                
                _event.Set();
                Automation.RemoveAutomationEventHandler(_eventId, _element, _eventHandler);
            }            
        }

        
public void Wait(int timeOut)
        {
            _event.WaitOne(timeOut);
        }

     public static void WaiterDemo()
        {
            DateTime startTime 
= DateTime.Now;
            CacheRequest cacheRequest 
= new CacheRequest();
            cacheRequest.TreeScope 
= TreeScope.Element;
            cacheRequest.Add(AutomationElement.NameProperty);

            
using (cacheRequest.Activate())
            {
                AutomationElement wpfRoot 
= AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
                AutomationElement btnOpenNewWindow 
= wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "ButtonOpenFlashWindow"));
                InvokePattern invokPtn 
= (InvokePattern)btnOpenNewWindow.GetCurrentPattern(InvokePattern.Pattern);

                SimpleWaiter waiter 
= new SimpleWaiter(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));

                invokPtn.Invoke();
                Console.WriteLine(
"Button Clicked and Wait...");
                waiter.Wait(
1000 * 1000);

                AutomationElement newWindow 
= AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));
                Console.WriteLine(newWindow.Current.Name);


                Console.WriteLine(
"Test finishes...................");
                Console.WriteLine(
"Test cost {0} seconds", (DateTime.Now - startTime).TotalSeconds);
            }
        }
    } 

 

 

测试演示3: 如何模拟真实的鼠标click, 以及如何确保测试目标位于前台:

 

代码
[StructLayout(LayoutKind.Sequential)]
    
public struct HARDWAREINPUT
    {
        
public uint msg;
        
public ushort paramL;
        
public ushort paramH;
    }

    [StructLayout(LayoutKind.Sequential)]
    
public struct KEYBDINPUT
    {
        
public ushort virtualKeyCode;
        
public ushort scanCode;
        
public uint flags;
        
public uint time;
        
public IntPtr extraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    
public struct MOUSEINPUT
    {
        
public int dx;
        
public int dy;
        
public uint mouseData;
        
public uint flags;
        
public uint time;
        
public IntPtr extraInfo;
    }


    [StructLayout(LayoutKind.Explicit)]
    
public struct InputTypeUnion
    {
        
// Fields
        [FieldOffset(0)]
        
public HARDWAREINPUT hi;
        [FieldOffset(
0)]
        
public KEYBDINPUT ki;
        [FieldOffset(
0)]
        
public MOUSEINPUT mi;
    }

    [StructLayout(LayoutKind.Sequential)]
    
public struct INPUT
    {
        
public uint type;
        
public InputTypeUnion data;
    }

    
class Clicker
    {
        [DllImport(
"user32.dll")]
        
static extern uint SendInput(uint inputCount, ref INPUT inputs, int inputSize);

        
static public void Click(Point point)
        {            
            MovePointTo (point.X,point.Y);            
            Click();
        }

        
static public Point GetClickPoint(AutomationElement ele)
        {            
            Point clickablePt;
            
if(ele.TryGetClickablePoint(out clickablePt))
            {
                
return clickablePt;
            }
            var boundingRect 
= ele.Current.BoundingRectangle;           
            
return new Point(boundingRect.X + (boundingRect.Width / 2), boundingRect.Y + (boundingRect.Height / 2));            
        }

        
static void MovePointTo(double absX, double absY)
        {
            INPUT input 
= new INPUT();
            var virtualScreen 
= System.Windows.Forms.SystemInformation.VirtualScreen;
            absX 
= (((absX - virtualScreen.X) + 0.5* 65536.0/ ((double)virtualScreen.Width);
            absY 
= (((absY - virtualScreen.Y) + 0.5* 65536.0/ ((double)virtualScreen.Height);
            input.type 
= 0;
            input.data.mi.dx 
= (int)absX;
            input.data.mi.dy 
= (int)absY;
            input.data.mi.flags 
= 0xc001;
            SendInput(
1ref input, Marshal.SizeOf(input));
        }

        
static void MouseDown()
        {
            INPUT input 
= new INPUT();
            input.type 
= 0;
            input.data.mi.flags 
= 2//DOWN
            input.data.mi.mouseData |= 1;
            SendInput(
1ref input, Marshal.SizeOf(input));
        }

        
static void MouseUp()
        {
            INPUT input 
= new INPUT();
            input.type 
= 0;
            input.data.mi.flags 
= 4//UP
            input.data.mi.mouseData |= 1;
            SendInput(
1ref input, Marshal.SizeOf(input));
        }

        
static void Click()
        {
            MouseDown();
            MouseUp();
        }
    }
 

 

 

代码
public static void ClickWithWaiterDemo()
        {
            DateTime startTime 
= DateTime.Now;
            CacheRequest cacheRequest 
= new CacheRequest();
            cacheRequest.TreeScope 
= TreeScope.Element;
            cacheRequest.Add(AutomationElement.NameProperty);

            
using (cacheRequest.Activate())
            {
                AutomationElement wpfRoot 
= AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
                WindowPattern wndptn 
= (WindowPattern)wpfRoot.GetCurrentPattern(WindowPattern.Pattern);

                var currentStates 
= wndptn.Current.WindowVisualState;
                
if (currentStates != WindowVisualState.Minimized)
                {
                    wndptn.SetWindowVisualState(currentStates);
                }
                
else
                {
                    wndptn.SetWindowVisualState(WindowVisualState.Normal);
                }
                    
                AutomationElement btnOpenNewWindow 
= wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "ButtonOpenFlashWindow"));
                InvokePattern invokPtn 
= (InvokePattern)btnOpenNewWindow.GetCurrentPattern(InvokePattern.Pattern);

                SimpleWaiter waiter 
= new SimpleWaiter(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));
                var poi 
= Clicker.GetClickPoint(btnOpenNewWindow);
                Clicker.Click(poi);
                
//invokPtn.Invoke();
                Console.WriteLine("Button Clicked and Wait...");
                waiter.Wait(
1000 * 1000);

                AutomationElement newWindow 
= AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));
                Console.WriteLine(newWindow.Current.Name);

                Console.WriteLine(
"Test finishes...................");
                Console.WriteLine(
"Test cost {0} seconds", (DateTime.Now - startTime).TotalSeconds);
            }
        }

 

 

测试演示4,5演示WaitForReady以及简单的Engine实现, 代码比较多, 就请自己下载吧.

 https://files.cnblogs.com/lixiong/UIAutoDemo.zip