2015-02-05 18 views
7

Sto provando a eseguire test automatici della mia applicazione tramite l'automazione dell'interfaccia utente (principalmente utilizzando TestStack.White per fornire un'interfaccia amichevole, utilizza System.Windows.Automation come back-end) . Ho una tabella con ~ 200 righe che ho bisogno di testare i valori di (in realtà voglio solo testare la prima e l'ultima coppia di righe). Ho scoperto che usando COM-Interop UIAutomationCore da solo, posso enumerare le righe in una frazione di secondo, ma solo quando non uso White o System.Windows.Automation. Appena System.Windows.Automation inizializza, future azioni di automazione interfaccia utente per enumerare le righe sono lenti:System.Windows.Automation è molto lento all'enumerazione delle righe della tabella rispetto a UIAutomationCore

First COM run: it took 0.04 seconds to get 102 rows! 
First System.Windows.Automation run: it took 7.18 seconds to get 102 rows! 
Second COM run: it took 7.87 seconds to get 102 rows! 

Ho creato una semplice applicazione di test WinForms (TableTest.exe per verificare che si trattava di System.Windows.Automation e non qualcosa a che fare con la mia domanda:

static void Main() 
{ 
    Application.EnableVisualStyles(); 
    Application.SetCompatibleTextRenderingDefault(false); 

    var form = new Form() { Text = "TableTest", WindowState = FormWindowState.Maximized }; 
    var dgv = new DataGridView() { Name = "DGV", Dock = DockStyle.Fill, AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill }; 
    dgv.Columns.Add("i", "i"); 
    dgv.Columns.Add("2i", "2i"); 
    dgv.Columns.Add("i^2", "i^2"); 
    dgv.Columns.Add("i^i", "i^i"); 
    for (int i = 0; i < 100; ++i) 
     dgv.Rows.Add(i, i * 2, i * i, Math.Pow(i, i)); 
    form.Controls.Add(dgv); 

    Application.Run(form); 
} 

Quindi ho creato un'altra app di test per testare il primo: funziona come un'app per console o un'app WinForms. Prima eseguo il test con l'automazione COM, quindi con System.Windows.Automation, quindi di nuovo con l'automazione COM. puoi vedere dall'output che ho riportato sopra, il primo blocco esegue molto qu I due blocchi successivi vengono eseguiti atrocemente lentamente. Se commento il codice di blocco System.Windows.Automation, entrambi i blocchi COM vengono eseguiti rapidamente.

using UIA = Interop.UIAutomationCore; 
static void Main(string[] args) 
{ 
    var process = System.Diagnostics.Process.Start("TableTest.exe"); 
    System.Threading.Thread.Sleep(500); 
    var uia = new UIA.CUIAutomation(); 
    var rootCom = uia.GetRootElement(); 
    var windowCom = rootCom.FindFirst(UIA.TreeScope.TreeScope_Children, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_NamePropertyId, "TableTest")); 
    var dgvCom = windowCom.FindFirst(UIA.TreeScope.TreeScope_Descendants, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_AutomationIdPropertyId, "DGV")); 
    var start = DateTime.Now; 
    var rowCount = dgvCom.FindAll(UIA.TreeScope.TreeScope_Children, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_ControlTypePropertyId, UIA.UIA_ControlTypeIds.UIA_CustomControlTypeId)).Length; 
    var elapsed = (DateTime.Now - start).TotalSeconds; 
    Console.WriteLine(String.Format("It took {0} seconds to get {1} rows!", elapsed.ToString("f2"), rowCount)); 
    process.Kill(); 

    process = System.Diagnostics.Process.Start("TableTest.exe"); 
    System.Threading.Thread.Sleep(500); 
    var root = AutomationElement.RootElement; 
    var window = root.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "TableTest")); 
    var dgv = window.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.AutomationIdProperty, "DGV")); 
    start = DateTime.Now; 
    rowCount = dgv.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom)).Count; 
     elapsed = (DateTime.Now - start).TotalSeconds; 
    Console.WriteLine(String.Format("It took {0} seconds to get {1} rows!", elapsed.ToString("f2"), rowCount)); 
     process.Kill(); 

    process = System.Diagnostics.Process.Start("TableTest.exe"); 
    System.Threading.Thread.Sleep(500); 
    uia = new UIA.CUIAutomation(); 
    rootCom = uia.GetRootElement(); 
    windowCom = rootCom.FindFirst(UIA.TreeScope.TreeScope_Children, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_NamePropertyId, "TableTest")); 
    dgvCom = windowCom.FindFirst(UIA.TreeScope.TreeScope_Descendants, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_AutomationIdPropertyId, "DGV")); 
    start = DateTime.Now; 
    rowCount = dgvCom.FindAll(UIA.TreeScope.TreeScope_Children, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_ControlTypePropertyId, UIA.UIA_ControlTypeIds.UIA_CustomControlTypeId)).Length; 
    elapsed = (DateTime.Now - start).TotalSeconds; 
    Console.WriteLine(String.Format("It took {0} seconds to get {1} rows!", elapsed.ToString("f2"), rowCount)); 
    process.Kill(); 
} 

Che diavolo sta facendo System.Windows.Automation che uccide le prestazioni di automazione interfaccia utente? Ho guardato il codice sorgente White e non vedo nulla di ovvio. Non riesco a profilare System.Windows.Automation stesso perché non riesco a trovare alcun PDB per questo. Non ho molta familiarità con l'automazione dell'interfaccia utente, quindi forse sarà ovvio per qualcun altro. Il bianco è: 0.13.0.0 e sto testando su Windows a 64 bit 7.

+0

ho riscritta mio esempio di utilizzare 'System.Windows.Automation' e ha scoperto che il problema è lì, non in bianco (che usa' System.Windows.Automation' piuttosto che l'interfaccia COM). Cioè quando si utilizza 'System.Windows.Automation', la prima enumerazione di riga è lenta quanto la seconda. Forse questo aiuta a restringere le cose? –

+0

Ho replicato il tuo problema ed è molto strano. Capisco perché il metodo COM è più veloce in quanto utilizza codice non gestito che attraverserà l'albero più rapidamente. Perché il metodo System.Windows.Automation dovrebbe eliminare le chiamate successive alle librerie COM anche se non ne ho idea. Non ho familiarità con White, ma è possibile utilizzarlo con questo wrapper http://uiacomwrapper.codeplex.com/? Questo ti darebbe l'interfaccia che White fornisce, con le prestazioni del metodo COM. –

+0

Ho fatto qualche ispezione del codice winforms e penso che ci siano molti molti messaggi di posta, messaggi di peek e attese coinvolti –

risposta

0

Gli esempi che hai postato non usano il bianco ... FWIW, il bianco usa le chiamate ricorsive all'automazione.Trova la richiesta ai bambini ogni volta. Ciò restituisce risultati validi ma è più lento di richiedere la sottostruttura dal nodo principale appropriato - nota che il nodo radice non è mai il nodo "appropriato" da cui richiedere la sottostruttura (vedere la nota su MSDN). Poiché il tuo esempio richiede una sola volta ai bambini, non è questo il problema.

2

Non posso rispondere alla tua domanda. Ma molte persone verranno da Google che cercano le parole chiave "uiautomation slow" e il primo risultato su Google è la tua domanda. (Hai scritto un bestseller)

Per tutti quelli che provengono da Google e che stanno lottando con l'UIAutomazione lenta pubblico questa risposta.

System.Windows.Automation è ESTREMAMENTE lento. Ottenere 30 elementi figlio può richiedere 1000ms su un computer molto veloce! L'ho persino visto sospeso per sempre mentre ricevevo gli elementi figlio di un albero in un'applicazione QT.

Oltre a ciò l'implementazione è non thread-safe.

System.Windows.Automation obsoleto. Non usarlo!

Nella MSDN si trova la seguente nota:

UI Automation per la prima volta disponibile in Windows XP come parte del Microsoft .NET Framework.Sebbene un'API C++ non gestita fosse anche pubblicata in quel momento, l'utilità delle funzioni client era limitata a a causa di problemi di interoperabilità. Per Windows 7, l'API è stata riscritta nel COM (Component Object Model). Sebbene le funzioni della libreria introdotte nella versione precedente di UI Automation siano ancora documentate, non dovrebbero essere utilizzate nelle nuove applicazioni .

La soluzione a rallentare le prestazioni è quello di utilizzare la nuova interfaccia COM IUIAutomationElement invece della vecchia interfaccia System.Windows.Automation C#. Dopo che il codice sarà in esecuzione fulmineo!

Oltre a ciò la nuova interfaccia offre molti più schemi e Microsoft la sta continuamente estendendo. Nell'SDK di Windows 10 (UIAutomationClient.h e UIAutomationCore.h) sono stati aggiunti diversi modelli e proprietà che non sono disponibili nel framework di automazione .NET.

I seguenti modelli sono disponibili nella versione COM di UIAutomation che non esistono in System.Windows.Automation:

  • IUIAutomationLegacyIAccessiblePattern
  • IUIAutomationObjectModelPattern
  • IUIAutomationAnnotationPattern
  • IUIAutomationTextPattern2
  • IUIAutomationStylesPattern
  • IUIAutomationSpreadsheetPattern
  • IUIAutomationSpreadsheetItemPattern
  • IUIAutomationTransformPattern2
  • IUIAutomationTextChildPattern
  • IUIAutomationDragPattern
  • IUIAutomationDropTargetPattern
  • IUIAutomationTextEditPattern
  • IUIAutomationCustomNavigationPattern

Inoltre sono stati aggiunti i seguenti tipi di controllo:

  • AppBar
  • SemanticZoom

Inoltre l'elemento seguente sono state aggiunte:

  • IUIAutomationElement2
  • IUIAutomationElement3
  • IUIAuto mationElement4
+0

Hai scritto un bestseller Sir! – Fals