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.
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? –
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. –
Ho fatto qualche ispezione del codice winforms e penso che ci siano molti molti messaggi di posta, messaggi di peek e attese coinvolti –