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

根据我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主窗口代码:

 

ExpandedBlockStart.gif 代码
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( 0 0 5 );
            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的实现:

 

ExpandedBlockStart.gif 代码
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( 0 0 ),  new  Point(RenderSize.Width  /   2 , RenderSize.Height)));
            dc.DrawRectangle(Brushes.Black, 
new  Pen(Brushes.Black,  10 ),  new  Rect( new  Point(RenderSize.Width  /   2 0 ),  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>

 

ExpandedBlockStart.gif 代码
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实现:

 

ExpandedBlockStart.gif 代码
[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(
this new  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:

 

ExpandedBlockStart.gif 代码
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:

 

ExpandedBlockStart.gif 代码
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的处理:

 

ExpandedBlockStart.gif 代码
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, 以及如何确保测试目标位于前台:

 

ExpandedBlockStart.gif 代码
[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(
1 ref  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(
1 ref  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(
1 ref  input, Marshal.SizeOf(input));
        }

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

 

 

ExpandedBlockStart.gif 代码
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实现, 代码比较多, 就请自己下载吧.

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

 

转载于:https://www.cnblogs.com/lixiong/archive/2010/05/20/1740335.html

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值