I'm trying to automate a part of our application WinForms/MFC application (to be able to perform automated tests) - however, currently I'm struggling with slow finding of the main menu (SysTreeView32 control) - basically the central component, which enables access to all the screens which I need to test.
I tried to find it with AutomationElement.FindFirst(), also with the TreeWalker.GetFirstChild(), but both methods seem far too slow.
What was interesting, once I started interaction with this TreeView during the time the test was trying to find the control - like expand/collapse some items, move with the mouse over the tested application - the control was found almost immediately.
What's the catch? Application is reacting very smoothly - only lookup process takes ages, when the app is inactive.
I had the same problem trying to find a child of my main window. You should give as many details as you can (filter things out) so that the search goes faster. In my case, I had a Save As Window, I added the following:
The window should be a ControlType.Window
AndCondition
The AutomationElement.NameProperty of the window should be: "Save As"
var saveWindow = appElement.FindFirst(TreeScope.Children | TreeScope.Descendants, new AndCondition(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window), new PropertyCondition(AutomationElement.NameProperty, "Save As")));
You need to do further tests to determine if the AutomationElement you are searching for can be found within the Descendants or the Children and skip search in one of them if necessary. In my case, the code above worked fine, but in a subsequent step, I required to find the Edit control / Text box next to "File name:" so I first created a condition:
var saveWindow = appElement.FindFirst(TreeScope.Children | TreeScope.Descendants, new AndCondition(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window), new PropertyCondition(AutomationElement.NameProperty, "Save As")));
And then added the condition to the search:
var fileName = saveWindow.FindFirst(TreeScope.Children | TreeScope.Descendants, fileNameCondition);
Strangely, and probably because of UIAutomation limitations, and unexpected behaviour, the line above was finding the element instantly in a Windows 10 Creators Update machine, but was getting stuck in a Windows 10 Anniversary Update... so after some debugging I found out that I could find the element by just searching in the children and that found it instantly in. both machines:
var fileName = saveWindow.FindFirst(TreeScope.Children, fileNameCondition);
Related
Was thinking it would be an easy task, but it proved to be far from that.
Spy++ doesn't generate control ID's (Only shows main window), each control has a name. Example(Button->"AddButton")
I've tried using invoked methods such as SendMessage, and i get the main window handle just fine, but always get a 0 when trying to get control handles.
I heard about UI Automation library, but didn't find any clear examples of usage for my specific task.
My Goal:Being able to retrieve data (Such as data grid cells texts), and click buttons on a WPF application, remotely from another application (I'll be using C#)
Thanks in advance!
EDIT: I've tried Ranorex Spy and was able to click the control just fine:
Press button from Ranorex
How can i simulate it?
EDIT 2:
I managed to do it after further reading some documentations, hopefully it will help other people:
string mainTitle = "";
string controlName = "";
AutomationElement prog = AutomationElement.RootElement.FindFirst(TreeScope.Children,
new PropertyCondition(AutomationElement.NameProperty, mainTitle));
AutomationElement btn = prog.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.AutomationIdProperty, controlName));
InvokePattern clickBtn = (InvokePattern) btn.GetCurrentPattern(InvokePattern.Pattern);
clickBtn.Invoke();
You can indeed use UI Automation which was (kinda) designed to do this kind of stuff. There is also a NuGet-Package which does some work for you already, called TestStack.White. Originally it is designed for automated UI testing, but I don't see why you couldn't use it for doing stuff like that. You can find out more about the Package here, and more about UI Automation here.
I'm googling and trying to solve this problem already one whole day, so I think it's right time to ask.
In my C# code, I'm trying to get hwnds of IE tabs in the same order as they are displayed in the browser.
Important note: I cannot do it with URL or Title since browser can contain two tabs with same page opened.
For that I tried different approaches:
I used Interop.SHDocVw library, where I'm trying to get all IE
windows and get their tab hwnd. This approach is working fine, but
tabs are ordered by date of creation. So if I open 4 tabs in IE it
returns me the right order, but when I move one tab to first
position, it's still shown as 4th in this approach.
List<int> hwnds = new List<int>();
ShellWindows shellWindows = new ShellWindows();
var explorer = shellWindows.Cast<InternetExplorer>()
.ToList()
.Where(item => item.Document is IHTMLDocument2);
foreach (var internetExplorer in explorer) {
hwnds.Add((internetExplorer.Document as IOleWindow).GetWindow());
}
return windows;
Other way was to implement Win32 windows searching, where I'm
filtering the windows by it's classname. This returns the order like
in Spy++ software. From my look it looks like it's sorted by last
interaction. First one is tab with which I interacted like the last,
second - previous last, etc.
Another thing that I tried was to implement IEAccessible way of getting
tabs, but that didn't work for me at all.
https://social.msdn.microsoft.com/Forums/ie/en-US/03a8c835-e9e4-405b-8345-6c3d36bc8941/ie7-ie8-tab-interaction-accessibility-class?forum=ieextensiondevelopment
Last thing that I tried is to use UI Automation, but with WinSDK
Inspect tool I could see that only hwnd of current opened tab is
visible. Otherwise I can see only button with titles without hwnds.
I would be really thankful if someone could help me with this problem - since I cannot see any other solution.
You should be able to get tabs in a proper order through UI Automation. Running Inspect.exe tool I am able to see them correctly under a lot of pane controls:
Sorry for disturbance but it seems that I need your help.
I am really beginner in C#, White framework.
Here is the problem...
I am trying to automate WinForm application, very basic.
Manual steps for automation:
Open window
Download file to this window
Find caption at bottom of this window
Problem: I can press any buttons, open dialogs and so on.
But after several steps I can't find label (caption).
I tried this code at 2 PCs. For one PC it works, for other - doesn't work.
I saw only one time that this label was found.
Usually I see that Studio just hang up and trying to search elements.
I think that Studio works very quickly and elements are not available for reading. Could you help me with this?enter image description here
//Add file name for opening
TextBox listLoadFile = mainWindow.Get<TextBox>(SearchCriteria.ByText("File name:"));
listLoadFile.Text = "Omneon_72.lst";
application.WaitWhileBusy();
// open file
Keyboard.Instance.PressSpecialKey(KeyboardInput.SpecialKeys.RETURN);
application.WaitWhileBusy();
mainWindow.Focus();
mainWindow.ReloadIfCached();
//Can't find this element
Label caption3 = mainWindow.Get<Label>(SearchCriteria.ByAutomationId("lblStatus"));
//Can't see elements
IUIItem[] children1 = mainWindow.GetMultiple(SearchCriteria.All);
What if instead of reloading mainWindow you'd just reattach the process again.
process = Process.GetProcessesByName("myProcess")[0];
application = Application.Attach(process.Id);
mainWindow = application.GetWindow("myWindowTitle");
Label caption3 = mainWindow.Get<Label>(SearchCriteria.ByAutomationId("lblStatus"));
IUIItem[] children1 = mainWindow.GetMultiple(SearchCriteria.All);
I need to automate 3rd party WPF application. I use
TestStack/White. This application has menu bar that is presented by images. After some actions menu is changing. There presents new images. When I want to click on new image:
Window mainWindow = application.GetWindow("Main window", InitializeOption.NoCache);
Image newTask = mainWindow.Get<Image>(SearchCriteria.ByControlType(ControlType.Image).AndIndex(2));
newTask.Click();
I get exception:
TestStack.White.AutomationException: Cannot perform action on Image.
AutomationId:, Name:, ControlType:image, FrameworkId:WPF, element is
offscreen.
I use Microsoft Inspect for research elements.
When I start tests, Inspect show me that image is offscreen. But if I do these actions manually, it works perfectly and in Inspect this image is not offscreen.
How can I refresh these elements or clear cache of window?
There are ReInitialize and ReloadIfCached methods on Window object. Try those to see if something changes.
Are you sure AndIndex(2) is correct element in that particular situation?
Try using GetMultiple and iterate the collection to see what images you actually have and which are not Offscreen.
WPF automation with White is pretty hard. Try Telerik Testing Framework and White could be supporting framework. It is much more easier that way.
It can be a problem with focus, try to use this before getting image:
mainWindow.Focus(DisplayState.Maximized);
Not an exact answer but the final solution I've got after all these TestStack.White not found elements, table rows, and so on. I started to move it to FlaUI. If something does not work or unstable with White then most likely I can get more stable and fast-executable FlaUI solution.
Fortunatly, such migration can be done with little steps. For example I already have TestStack.White.Application app, then I replace White portion of code with FlaUI like this:
var flApp = FlaUI.Core.Application.Attach(app.Process.Id);
using (var automation = new UIA3Automation())
{
// .. new UI element processing
}
I don’t think that caching is the problem here. You are getting the mainWindow with InitializeOption.NoCache. In Non-cache mode the controls are found on demand. So I presume that the cache is refreshed automatically. (https://github.com/TestStack/White/blob/master/src/TestStack.White/Factory/InitializeOption.cs)
Perhaps the index of the element you want to click is not 2.
Have you tried adding an explicit wait? It sounds like you have only tried adding an implicit wait.(https://github.com/TestStack/TestStack.docs/blob/master/_source/White/Advanced%20Topics/Waiting.md)
Our application has many controls that are created dynamically. For example, a navigation pane contains groups of links that change as the user navigates through the app. The first time I use AccExplorer to select these links, it finds them perfectly. However, after I navigate to another page, AccExplorer can't see the links in the updated navigation pane. In fact, the old link controls still appear in the AccExplorer tree hierarchy and as I click on them AccExplorer highlights areas of the desktop that are outside the bounds of our application's actual window.
The controls have changed, but AccExplorer doesn't recognize them. It still thinks the hierarchy is the same.
What I find strange is that closing AccExplorer and then opening a new instance of AccExplorer results in the same results. When I select the new navigation control with AccExplorer, it doesn't see the new links. It recreates the old hierarchy with the old links. The only way to see the new links is if I close our application, reopen our application, manually navigate to the new page, then select the controls with AccExplorer.
The navigation control is a DevExpress control. OS is WinXP. AccExplorer 2.0. I'd prefer to use UIAutomation, but not all DevExpress controls support UIAutomation. We're trying to use MSAA to fill in those gaps.
To follow up...
With UISpy I'm able to find a control called LeftNavExplorerBarGroups that AccExplorer cannot see. I'm able to use UIAutomation to get the window handle of the control then use the handle to find the IAccessible object with MSAALayer. The fact that AccExplorer can't see that control and that the accName is null made it very difficult to discover how to find the controls I needed. (Note: MSAA comes from Arshad - http://www.codeproject.com/KB/winsdk/MSAA_UI_Automation.aspx)
AutomationElement a, b;
Process p;
Process[] existingProcesses;
IAccessible c;
existingProcesses = Process.GetProcessesByName("OurApp");
if (existingProcesses.Length > 0) {
p = existingProcesses[0];
a = AutomationElement.FromHandle(p.MainWindowHandle);
b = a.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.AutomationIdProperty, "LeftNavExplorerBarGroups"));
c = MSAA.GetAccessibleObjectFromHandle(new IntPtr(b.Current.NativeWindowHandle));
}