Update native bridge to UI Automation for speed and reliability

Native UI Automation bridge redesigned for speed and reliability.  The
bridge library has been renamed to UiaBridge.  Native components use VS
2010 C++ and .Net 4.
This commit is contained in:
Edward Jakubowski
2014-04-29 23:33:10 -04:00
parent 7a267a6d9a
commit acf215fe00
48 changed files with 1453 additions and 1105 deletions

View File

@@ -195,14 +195,142 @@ public class RobotMacro {
lastException = e;
}
}
/* SendKeys Special Keys List
{BACKSPACE}, {BS}, or {BKSP}
{BREAK}
{CAPSLOCK}
{DELETE} or {DEL}
{DOWN}
{END}
{ENTER} or ~
{ESC}
{HELP}
{HOME}
{INSERT} or {INS}
{LEFT}
{NUMLOCK}
{PGDN}
{PGUP}
{PRTSC} (reserved for future use)
{RIGHT}
{SCROLLLOCK}
{TAB}
{UP}
{F1}
{F2}
{F3}
{F4}
{F5}
{F6}
{F7}
{F8}
{F9}
{F10}
{F11}
{F12}
{F13}
{F14}
{F15}
{F16}
{ADD}
{SUBTRACT}
{MULTIPLY}
{DIVIDE}
{{}
{}}
SHIFT +
CTRL ^
ALT %
*/
public static boolean sendKeys(String keyCommands) {
try {
Robot robot = new Robot();
boolean specialKeyFlag = false;
String specialKey = "";
boolean modifierKeyFlag = false;
String modifierKeys = "";
for (int i = 0; i < keyCommands.length(); i++) {
char key = keyCommands.charAt(i);
int[] keyCode = getKeyCode(key);
pressKeyCodes(robot, keyCode);
if (specialKeyFlag)
specialKey += key;
if (key == '{' && specialKeyFlag == false) {
specialKeyFlag = true;
specialKey = "{";
}
if (!specialKeyFlag) { //not special key(tab,enter,...) just press normal keys and modifiers
// Modifier key logic
if (key == '+' || key == '^' || key == '%') { //shift alt or ctrl
if (!modifierKeyFlag) {
modifierKeys = key + "";
modifierKeyFlag = true;
}
else
modifierKeys += key + ""; //append multiple modifiers
if (key == '+')
robot.keyPress(KeyEvent.VK_SHIFT);
if (key == '^')
robot.keyPress(KeyEvent.VK_CONTROL);
if (key == '%')
robot.keyPress(KeyEvent.VK_ALT);
continue; //skip to next key
}
pressKeyCodes(robot, getKeyCode(key));
}
if (specialKeyFlag) {
if (specialKey.equals("{ENTER}")) {
specialKeyFlag = false;
pressKeyCodes(robot, new int[]{KeyEvent.VK_ENTER} );
}
else if (specialKey.equals("{ESC}")) {
specialKeyFlag = false;
pressKeyCodes(robot, new int[]{KeyEvent.VK_ESCAPE} );
}
else if (specialKey.equals("{HOME}")) {
specialKeyFlag = false;
pressKeyCodes(robot, new int[]{KeyEvent.VK_HOME} );
}
else if (specialKey.equals("{END}")) {
specialKeyFlag = false;
pressKeyCodes(robot, new int[]{KeyEvent.VK_END} );
}
else if (specialKey.equals("{PGDN}")) {
specialKeyFlag = false;
pressKeyCodes(robot, new int[]{KeyEvent.VK_PAGE_DOWN} );
}
else if (specialKey.equals("{PGUP}")) {
specialKeyFlag = false;
pressKeyCodes(robot, new int[]{KeyEvent.VK_PAGE_UP} );
}
else if (specialKey.equals("{TAB}")) {
specialKeyFlag = false;
pressKeyCodes(robot, new int[]{KeyEvent.VK_TAB} );
}
else if (specialKey.equals("{UP}")) {
specialKeyFlag = false;
pressKeyCodes(robot, new int[]{KeyEvent.VK_UP} );
}
else if (specialKey.equals("{DOWN}")) {
specialKeyFlag = false;
pressKeyCodes(robot, new int[]{KeyEvent.VK_DOWN} );
}
}
if (modifierKeyFlag) { //time to release all the modifier keys
modifierKeyFlag = false;
for (int m = 0; m < modifierKeys.length(); m++) {
char mkey = modifierKeys.charAt(m);
if (mkey == '+')
robot.keyRelease(KeyEvent.VK_SHIFT);
if (mkey == '^')
robot.keyRelease(KeyEvent.VK_CONTROL);
if (mkey == '%')
robot.keyRelease(KeyEvent.VK_ALT);
}
modifierKeys = "";
}
}
} catch (Exception e) {
lastException = e;

View File

@@ -71,7 +71,7 @@ public class SynthuseDlg extends JFrame {
/**
*
*/
public static String VERSION_STR = "1.1.0";
public static String VERSION_STR = "1.1.2";
public static String RES_STR_MAIN_ICON = "/org/synthuse/img/gnome-robots.png";
public static String RES_STR_REFRESH_IMG = "/org/synthuse/img/rapidsvn.png";
@@ -102,7 +102,7 @@ public class SynthuseDlg extends JFrame {
private TestIdeFrame testIde = null;
private int targetX;
private int targetY;
private WpfBridge wpf = new WpfBridge();
private UiaBridge uiabridge = new UiaBridge();
/**
* Launch the application.
@@ -449,7 +449,8 @@ public class SynthuseDlg extends JFrame {
String handleStr = Api.GetHandleAsString(hwnd);
String classStr = WindowsEnumeratedXml.escapeXmlAttributeValue(Api.GetWindowClassName(hwnd));
String parentStr = Api.GetHandleAsString(User32.instance.GetParent(hwnd));
String runtimeId = wpf.getRuntimeIdFromPoint(targetX, targetY);
String enumProperties = uiabridge.getWindowInfo(targetX, targetY, WindowInfo.UIA_PROPERTY_LIST);
String runtimeId = WindowInfo.getRuntimeIdFromProperties(enumProperties);
lblStatus.setText("rid:" + runtimeId + " class: " + classStr + " hWnd: " + handleStr + " parent: " + parentStr + " X,Y: " + targetX + ", " + targetY);
if (!lastDragHwnd.equals(handleStr) || !lastRuntimeId.equals(runtimeId)) {
if (!lastDragHwnd.isEmpty()) {
@@ -459,7 +460,7 @@ public class SynthuseDlg extends JFrame {
lastRuntimeId = runtimeId;
//lastDragHwnd = (hwnd + "");
Api.highlightWindow(hwnd);
XpathManager.buildXpathStatementThreaded(hwnd, runtimeId, textPane, xpathEvents);
XpathManager.buildXpathStatementThreaded(hwnd, enumProperties, textPane, xpathEvents);
}
}

View File

@@ -1,23 +1,14 @@
/*
* Copyright 2014, Synthuse.org
* Released under the Apache Version 2.0 License.
*
* last modified by ejakubowski7@gmail.com
*/
package org.synthuse;
import java.awt.Point;
import java.io.*;
public class WpfBridge {
public class UiaBridge {
static
{
String archDataModel = System.getProperty("sun.arch.data.model");//32 or 64 bit
//System.loadLibrary("native/WpfBridge" + archDataModel); // WpfBridge32.dll (Windows) or WpfBridge32.so (Unixes)
loadNativeLibraryFromJar("/wpfbridge" + archDataModel + ".dll");
loadNativeLibraryFromJar("/uiabridge" + archDataModel + ".dll");
}
public static void loadNativeLibraryFromJar(String path) {
@@ -49,7 +40,7 @@ public class WpfBridge {
byte[] buffer = new byte[1024];
int readBytes;
// Open and check input stream
InputStream is = WpfBridge.class.getResourceAsStream(path);
InputStream is = UiaBridge.class.getResourceAsStream(path);
if (is == null) { //check if valid
System.out.println("File " + path + " was not found inside JAR.");
return;
@@ -69,7 +60,27 @@ public class WpfBridge {
// Finally, load the library
System.load(temp.getAbsolutePath());
}
public UiaBridge ()
{
initialize("");
}
public native void initialize(String properties);
public native void shutdown();
public native int addEnumFilter(String propertyName, String propertyValue);
public native void clearEnumFilters();
public native String[] enumWindowInfo(String properties);
public native String[] enumWindowInfo(int windowHandle, String properties);
//native String[] enumWindowInfo(AutomationElement ^element, String properties);
//native String[] enumWindowInfo(AutomationElement ^element, String properties, String[] filterModifierList);
//native String getWindowInfo(AutomationElement ^element, String properties);
public native String getWindowInfo(int x, int y, String properties);
public native String getWindowInfo(int windowHandle, String properties);
public native String getWindowInfo(String runtimeId, String properties);
/*
public native void setFrameworkId(String propertyValue); //default is WPF, but also accepts Silverlight, Win32
public native void setTouchableOnly(boolean val); //default is true
@@ -80,7 +91,7 @@ public class WpfBridge {
public native int countChildrenWindows();
public native int countChildrenWindows(String runtimeIdValue);
public native String[] enumChildrenWindowIds(String runtimeIdValue); //if runtimeIdValue is null will start at desktop
public String[] enumChildrenWindowIds(String runtimeIdValue); //if runtimeIdValue is null will start at desktop
public native String[] enumDescendantWindowIds(String runtimeIdValue); //if runtimeIdValue is null will start at desktop
public native String[] enumDescendantWindowIds(long processId);
//In all the above Enumerate methods will return a list of Runtime Ids for all related windows.
@@ -92,11 +103,12 @@ public class WpfBridge {
public native String getProperty(String propertyName, String runtimeIdValue);
public native String[] getProperties(String runtimeIdValue);
public native String[] getPropertiesAndValues(String runtimeIdValue);
*/
public Point getCenterOfElement(String runtimeIdValue) {
Point p = new Point();
String boundary = getProperty("BoundingRectangleProperty", runtimeIdValue);
//System.out.println("boundary: " + boundary); //boundary: 841,264,125,29
String boundary = getWindowInfo(runtimeIdValue, "BoundingRectangleProperty");
boundary = WindowInfo.replaceEscapedCodes(boundary);
//System.out.println("runtimeId: " + runtimeIdValue + ", boundary: " + boundary); //boundary: 841,264,125,29
String[] boundarySplt = boundary.split(",");
int x = Integer.parseInt(boundarySplt[0]);
int y = Integer.parseInt(boundarySplt[1]);
@@ -106,25 +118,4 @@ public class WpfBridge {
p.y = ((height) /2) + y;
return p;
}
public String getWindowClass(String runtimeIdValue) {
return getProperty("ClassNameProperty", runtimeIdValue);
}
public String getWindowText(String runtimeIdValue) {
return getProperty("NameProperty", runtimeIdValue);
}
public String getWindowValue(String runtimeIdValue) {
return getProperty("ValueProperty", runtimeIdValue);
}
public String getWindowAutomationId(String runtimeIdValue) {
return getProperty("AutomationIdProperty", runtimeIdValue);
}
public String getWindowFramework(String runtimeIdValue) {
return getProperty("FrameworkIdProperty", runtimeIdValue);
}
}

View File

@@ -24,7 +24,9 @@ import com.sun.jna.ptr.PointerByReference;
public class WindowInfo {
public static String WPF_PROPERTY_LIST = "RuntimeIdProperty,ParentRuntimeIdProperty,ProcessIdProperty,FrameworkIdProperty,ClassNameProperty,NameProperty,ValueProperty";
public static String UIA_PROPERTY_LIST = "RuntimeIdProperty,ParentRuntimeIdProperty,ProcessIdProperty,FrameworkIdProperty,LocalizedControlTypeProperty,ClassNameProperty,NameProperty,ValueProperty";
public static String UIA_RUNTIME_ID = "RuntimeIdProperty";
public static int MAX_TEXT_SIZE = 200;
public HWND hwnd;
public String hwndStr = "";
@@ -33,6 +35,7 @@ public class WindowInfo {
public RECT rect;
public String text;
public String value;
public String controlType = "";
public String className = "";
public boolean isChild = false;
public String processName = "";
@@ -53,9 +56,11 @@ public class WindowInfo {
text = Native.toString(buffer);
if (text.isEmpty())
text = new Api().sendWmGetText(hWnd);
if (text.isEmpty()) {
//if (text.isEmpty()) {
//System.out.println("getting toolbar text");
}
//}
if (text.length() > MAX_TEXT_SIZE) //if text is too large it will slow down xml display
text = text.substring(0, MAX_TEXT_SIZE);
//Get item count depending on what type of control it is
LRESULT tbCount = Api.User32.instance.SendMessage(hWnd, Api.TB_BUTTONCOUNT, new WPARAM(0), new LPARAM(0));
@@ -146,9 +151,11 @@ public class WindowInfo {
}
this.hwnd = hWnd;
hwndStr = Api.GetHandleAsString(hWnd);
if (this.hwndStr == null)
this.hwndStr = "";
}
public String replaceEscapedCodes(String input) {
public static String replaceEscapedCodes(String input) {
//&#44; is a comma ,
String result = input;
result = result.replaceAll("&#44;", ",");
@@ -160,9 +167,9 @@ public class WindowInfo {
return result;
}
//support for WPF and Silverlight
//support for WPF, Silverlight, WinForms
public WindowInfo(String enumProperties, boolean isChild) {
//WPF_PROPERTY_LIST = "RuntimeIdProperty,ParentRuntimeIdProperty,ProcessIdProperty,FrameworkIdProperty,ClassNameProperty,NameProperty";
//WPF_PROPERTY_LIST = "RuntimeIdProperty,ParentRuntimeIdProperty,ProcessIdProperty,FrameworkIdProperty,LocalizedControlTypeProperty,ClassNameProperty,NameProperty,ValueProperty";
String[] spltProperties = enumProperties.split(",");
this.isChild = isChild;
if (SynthuseDlg.config.isFilterWpfDisabled()) { //use wildcard mode
@@ -180,6 +187,8 @@ public class WindowInfo {
this.pid = Long.parseLong(propertyNameAndValue[1]);
else if (propertyNameAndValue[0].equals("FrameworkIdProperty"))
this.framework = propertyNameAndValue[1];
else if (propertyNameAndValue[0].equals("LocalizedControlTypeProperty"))
this.controlType = propertyNameAndValue[1];
else if (propertyNameAndValue[0].equals("ClassNameProperty"))
this.className = propertyNameAndValue[1];
else if (propertyNameAndValue[0].equals("NameProperty"))
@@ -191,6 +200,13 @@ public class WindowInfo {
}
}
this.hwndStr = this.runtimeId;
if (text != null)
if (text.length() > MAX_TEXT_SIZE)
text = text.substring(0, MAX_TEXT_SIZE);
if (this.hwndStr == null)
this.hwndStr = "";
//if (this.framework == null)
// this.framework = "na";
return;
}
// non-wildcard mode
@@ -204,11 +220,21 @@ public class WindowInfo {
if (spltProperties.length > 3)
this.framework = spltProperties[3];
if (spltProperties.length > 4)
this.className = replaceEscapedCodes(spltProperties[4]);
this.controlType = replaceEscapedCodes(spltProperties[4]);
if (spltProperties.length > 5)
this.text = replaceEscapedCodes(spltProperties[5]);
this.className = replaceEscapedCodes(spltProperties[5]);
if (spltProperties.length > 6)
this.value = replaceEscapedCodes(spltProperties[6]);
this.text = replaceEscapedCodes(spltProperties[6]);
if (spltProperties.length > 7)
this.value = replaceEscapedCodes(spltProperties[7]);
if (this.className == "")
this.className = this.controlType;
if (text != null)
if (text.length() > MAX_TEXT_SIZE)
text = text.substring(0, MAX_TEXT_SIZE);
if (this.hwndStr == null)
this.hwndStr = "";
/*
this.rect = new RECT();
try {
@@ -224,8 +250,16 @@ public class WindowInfo {
*/
}
public static String getRuntimeIdFromProperties(String enumProperties)
{
String[] spltProperties = enumProperties.split(",");
if (spltProperties.length > 0)
return spltProperties[0];
return "";
}
public String toString() {
return String.format("(%d,%d)-(%d,%d) : \"%s\" [%s] {%s}", rect.left,rect.top,rect.right,rect.bottom,text,className,hwnd.toString());
return String.format("%s \"%s\" [%s] (%s) {%s}", framework, text, className, controlType, hwndStr);
}
}

View File

@@ -45,6 +45,7 @@ import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinDef.HWND;
public class WindowsEnumeratedXml implements Runnable{
public static Exception lastException = null;
public static AtomicBoolean enumeratingXmlFlag = new AtomicBoolean(false);
public JTextPane outputPane = null;
@@ -81,10 +82,16 @@ public class WindowsEnumeratedXml implements Runnable{
final Map<String, String> processList = new LinkedHashMap<String, String>();
final List<String> wpfParentList = new ArrayList<String>();//HwndWrapper
final List<String> silverlightParentList = new ArrayList<String>();//MicrosoftSilverlight
final List<String> winFormParentList = new ArrayList<String>();//class="WindowsForms*"
int wpfCount = 0;
int winFormCount = 0;
int silverlightCount = 0;
int menuCount = 0;
//wpf.setTouchableOnly(false);
//wpf.countChildrenWindows();//fix for missing cached elements
class ChildWindowCallback implements WinUser.WNDENUMPROC {
@Override
public boolean callback(HWND hWnd, Pointer lParam) {
@@ -105,22 +112,31 @@ public class WindowsEnumeratedXml implements Runnable{
infoList.put(wi.hwndStr, wi);
if (wi.className.startsWith("HwndWrapper"))
wpfParentList.add(wi.hwndStr);
if (wi.className.startsWith("WindowsForms"))
winFormParentList.add(wi.hwndStr);
Api.User32.instance.EnumChildWindows(hWnd, new ChildWindowCallback(), new Pointer(0));
return true;
}
}
Api.User32.instance.EnumWindows(new ParentWindowCallback(), 0);
//Enumerate WPF windows and add to list
//Enumerate WPF, WinForm, Silverlight windows and add to list
if (!SynthuseDlg.config.isWpfBridgeDisabled())
{
UiaBridge uiabridge = new UiaBridge();
for (String handle : wpfParentList) {
Map<String, WindowInfo> wpfInfoList = EnumerateWindowsWithWpfBridge(handle, "WPF");
Map<String, WindowInfo> wpfInfoList = EnumerateWindowsWithUiaBridge(uiabridge, handle, "*");
wpfCount += wpfInfoList.size();
infoList.putAll(wpfInfoList);
}
for (String handle : winFormParentList) {
//System.out.println("winform parent " + handle);
Map<String, WindowInfo> winFormInfoList = EnumerateWindowsWithUiaBridge(uiabridge, handle, "*");
winFormCount += winFormInfoList.size();
infoList.putAll(winFormInfoList);
}
for (String handle : silverlightParentList) {
Map<String, WindowInfo> slInfoList = EnumerateWindowsWithWpfBridge(handle, "Silverlight");
Map<String, WindowInfo> slInfoList = EnumerateWindowsWithUiaBridge(uiabridge, handle, "Silverlight");
silverlightCount += slInfoList.size();
infoList.putAll(slInfoList);
}
@@ -147,8 +163,13 @@ public class WindowsEnumeratedXml implements Runnable{
win = doc.createElement("win");
else if (w.framework.equals("WPF"))
win = doc.createElement("wpf");
else if (w.framework.equals("WinForm"))
win = doc.createElement("winfrm");
else if (w.framework.equals("Silverlight"))
win = doc.createElement("silver");
else
win = doc.createElement("win");
//System.out.println(w.toString());
win.setAttribute("hwnd", w.hwndStr);
win.setAttribute("text", w.text);
@@ -159,6 +180,9 @@ public class WindowsEnumeratedXml implements Runnable{
win.setAttribute("class", w.className);
if (w.className != null)
win.setAttribute("CLASS", w.className.toUpperCase());
if (w.controlType != null)
if (!w.controlType.isEmpty())
win.setAttribute("type", w.controlType);
if (!w.isChild) {
parentCount++;
if (w.processName != null && !w.processName.isEmpty()) {
@@ -207,6 +231,7 @@ public class WindowsEnumeratedXml implements Runnable{
totals.setAttribute("windowCount", infoList.size()+"");
totals.setAttribute("wpfWrapperCount", wpfParentList.size()+"");
totals.setAttribute("wpfCount", wpfCount+"");
totals.setAttribute("winFormCount", winFormCount+"");
totals.setAttribute("silverlightCount", silverlightCount+"");
totals.setAttribute("menuCount", menuCount+"");
totals.setAttribute("processCount", processList.size()+"");
@@ -242,22 +267,19 @@ public class WindowsEnumeratedXml implements Runnable{
return xmlElement;
}
public static Map<String, WindowInfo> EnumerateWindowsWithWpfBridge(String parentHwndStr, String frameworkType) {
public static Map<String, WindowInfo> EnumerateWindowsWithUiaBridge(UiaBridge uiabridge, String parentHwndStr, String frameworkType) {
final Map<String, WindowInfo> infoList = new LinkedHashMap<String, WindowInfo>();
WpfBridge wb = new WpfBridge();
wb.setFrameworkId(frameworkType);
if (SynthuseDlg.config.isFilterWpfDisabled())
wb.setTouchableOnly(false);
//WpfBridge wb = new WpfBridge();
//wpf.setFrameworkId(frameworkType);
long hwnd = Long.parseLong(parentHwndStr);
//List<String> parentIds = new ArrayList<String>(Arrays.asList(wb.enumChildrenWindowIds("")));
//System.out.println("getRuntimeIdFromHandle");
String parentRuntimeId = wb.getRuntimeIdFromHandle(hwnd);
//System.out.println("getRuntimeIdFromHandle of " + hwnd);
String parentRuntimeId = uiabridge.getWindowInfo((int) hwnd, WindowInfo.UIA_RUNTIME_ID);
//System.out.println("runtimeId=" + runtimeId);
String[] allIds = null;
if (SynthuseDlg.config.isFilterWpfDisabled())
allIds = wb.enumDescendantWindowInfo(parentRuntimeId, "*");
allIds = uiabridge.enumWindowInfo((int) hwnd, "*");
else
allIds = wb.enumDescendantWindowInfo(parentRuntimeId, WindowInfo.WPF_PROPERTY_LIST);
allIds = uiabridge.enumWindowInfo((int) hwnd, WindowInfo.UIA_PROPERTY_LIST);
if (allIds == null)
return infoList; //empty list
//System.out.println("enumDescendantWindowIds " + allIds.length);

View File

@@ -21,9 +21,8 @@ import com.sun.jna.platform.win32.WinDef.HWND;
public class XpathManager implements Runnable{
private HWND hwnd = null;
private String runtimeId = null;
private String enumProperties = null;
private JTextPane windowsXmlTextPane = null;
private WpfBridge wpf = null;
public static interface Events {
void statusChanged(String status);
@@ -44,12 +43,11 @@ public class XpathManager implements Runnable{
this.windowsXmlTextPane = windowsXmlTextPane;
}
public XpathManager(HWND hwnd, String runtimeId, JTextPane windowsXmlTextPane, Events events) {
public XpathManager(HWND hwnd, String enumProperties, JTextPane windowsXmlTextPane, Events events) {
this.events = events;
this.hwnd = hwnd;
this.runtimeId = runtimeId;
this.enumProperties = enumProperties;
this.windowsXmlTextPane = windowsXmlTextPane;
this.wpf = new WpfBridge();
}
@Override
@@ -78,44 +76,46 @@ public class XpathManager implements Runnable{
return escapedTxtStr;
}
public String buildWpfXpathStatement() {
public String buildUiaXpathStatement() {
if (enumProperties == null)
return "";
if (enumProperties.isEmpty())
return "";
String builtXpath = "";
String xml = this.windowsXmlTextPane.getText();
String classStr = wpf.getWindowClass(runtimeId);
//System.out.println("class: " + classStr);
String txtOrig = wpf.getWindowText(runtimeId);
String winValueOrig = wpf.getWindowValue(runtimeId);
if (classStr == null || txtOrig == null)
WindowInfo wi = new WindowInfo(enumProperties, true);
String onlyRuntimeIdXpath = "//*[@hwnd='" + wi.runtimeId + "']";
List<String> wpfResultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, onlyRuntimeIdXpath);
//System.out.println("evaluateXpathGetValues1: " + onlyRuntimeIdXpath + " = " + wpfResultList.size());
if (wpfResultList.size() == 0)
return"";
//System.out.println("enumProperties: " + enumProperties);
String typeStr = wi.controlType;
String txtOrig = wi.text;
//String winValueOrig = wpf.getWindowValue(runtimeId);
if (typeStr == null || txtOrig == null)
return "";
//System.out.println("text: " + txtOrig);
String txtStr = compareLongTextString(txtOrig);
String valueStr = "";
if (winValueOrig != null)
if (!winValueOrig.isEmpty()) //if value attribute exists then use it too
valueStr = " and starts-with(@value,'" + compareLongTextString(winValueOrig) + "')";
builtXpath = "//*[@class='" + classStr + "' and starts-with(@text,'" + txtStr + "')" + valueStr + "]";
builtXpath = "//*[@type='" + typeStr + "' and starts-with(@text,'" + txtStr + "')" + "]";
//builtXpath = "//*[@hwnd='" + runtimeId + "']";
//System.out.println("evaluateXpathGetValues: " + builtXpath);
List<String> wpfResultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath);
wpfResultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath);
//System.out.println("evaluateXpathGetValues2: " + builtXpath + " = " + wpfResultList.size());
if (wpfResultList.size() == 1)
return builtXpath;
builtXpath = "//*[@hwnd='" + runtimeId + "']";
wpfResultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath);
if (wpfResultList.size() > 0)
return builtXpath;
return "";
return onlyRuntimeIdXpath;
}
public String buildXpathStatement() {
String builtXpath = "";
try {
String xml = this.windowsXmlTextPane.getText();
if (runtimeId != null && !SynthuseDlg.config.isWpfBridgeDisabled()) {
if (!runtimeId.isEmpty()) {
builtXpath = buildWpfXpathStatement();
if (enumProperties != null && !SynthuseDlg.config.isWpfBridgeDisabled()) {
if (!enumProperties.isEmpty()) {
builtXpath = buildUiaXpathStatement();
}
}
if (builtXpath != "")
@@ -164,6 +164,9 @@ public class XpathManager implements Runnable{
}
builtXpath = "//win[@class='" + classStr + "'" + txtStr + "]";
}
resultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath);
if (resultList.size() > 1) //still too many matched, only use hwnd
builtXpath = "//win[@hwnd='" + handleStr + "']";
}
} catch (Exception e) {
e.printStackTrace();

View File

@@ -16,7 +16,7 @@ public class BaseCommand {
static long LAST_UPDATED_XML = 0;
protected Api api = new Api();
protected WpfBridge wpf = new WpfBridge();
protected UiaBridge wpf = new UiaBridge();
protected CommandProcessor parentProcessor = null;
protected int getExecuteErrorCount() {
@@ -116,6 +116,7 @@ public class BaseCommand {
resultStr = item;
break;
}
resultStr = resultStr.replaceAll("[^\\d-.]", ""); //remove all non-numeric values (except dash -)
if (WinPtr.isWpfRuntimeIdFormat(resultStr)) {
result.runtimeId = resultStr;
if (!ignoreFailedFind && result.isEmpty())
@@ -143,7 +144,7 @@ public class BaseCommand {
if (item.contains("hmenu=")) {
List<String> list = WindowsEnumeratedXml.evaluateXpathGetValues(item, "//@id");
if (list.size() > 0)
resultStr = list.get(0); //get first id;
resultStr = list.get(0); //get first id
}
else
resultStr = item;

View File

@@ -54,7 +54,7 @@ public class UnitTestHelper {
byte[] buffer = new byte[1024];
int readBytes;
// Open and check input stream
InputStream is = WpfBridgeTest.class.getResourceAsStream(path);
InputStream is = UnitTestHelper.class.getResourceAsStream(path);
if (is == null) { //check if valid
System.out.println("File " + path + " was not found inside JAR.");
return null;

View File

@@ -1,125 +0,0 @@
package org.synthuse.test;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.synthuse.*;
public class WpfBridgeTest {
@BeforeClass
public static void setUpBeforeClass() {
//this runs only once for this class
UnitTestHelper.RunApp("/org/synthuse/test/WpfMockTestApp.exe");
}
@AfterClass
public static void tearDownAfterClass() {
//this runs only once for this class
UnitTestHelper.DestroyApp();
}
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void countChildrenWin32() {
WpfBridge wb = new WpfBridge();
wb.setFrameworkId("Win32");//We should find some Win32 windows, maybe not WPF
int win32Cnt = wb.countChildrenWindows();
System.out.println("win32 countChildrenWindows: " + win32Cnt);
assertTrue(win32Cnt > 0);
wb.setTouchableOnly(false);//disable filter
int ufwin32Cnt = wb.countChildrenWindows();
System.out.println("win32 unfiltered countChildrenWindows: " + ufwin32Cnt);
assertTrue(ufwin32Cnt >= win32Cnt);
}
@Test
public void countChildrenWpf() {
WpfBridge wb = new WpfBridge();
wb.setFrameworkId("WPF");// maybe not WPF
wb.setTouchableOnly(true);//enable filter
System.out.println("wpf countChildrenWindows: " + wb.countChildrenWindows());
wb.setTouchableOnly(false);//disable filter
System.out.println("wpf unfiltered countChildrenWindows: " + wb.countChildrenWindows());
}
//@Test
public void countDescendantsWin32() {
WpfBridge wb = new WpfBridge();
wb.setFrameworkId("Win32");//We should find some Win32 windows, maybe not WPF
int win32Cnt = wb.countDescendantWindows();
System.out.println("win32 countDescendantWindows: " + win32Cnt);
assertTrue(win32Cnt > 0);
wb.setTouchableOnly(false);//disable filter
int ufwin32Cnt = wb.countDescendantWindows();
System.out.println("win32 unfiltered countDescendantWindows: " + ufwin32Cnt);
assertTrue(ufwin32Cnt >= win32Cnt);
}
//@Test
public void countDescendantsWpf() {
WpfBridge wb = new WpfBridge();
wb.setFrameworkId("WPF");// maybe not WPF
wb.setTouchableOnly(true);//enable filter
System.out.println("wpf countDescendantWindows: " + wb.countDescendantWindows());
wb.setTouchableOnly(false);//disable filter
System.out.println("wpf unfiltered countDescendantWindows: " + wb.countDescendantWindows());
}
@Test
public void getRuntimeFromHandle() {
WpfBridge wb = new WpfBridge();
Api api = new Api();
wb.setFrameworkId("WPF");
WinPtr wp = new WinPtr(api.user32.FindWindow(null, "MainWindow"));//find WpfMockTestApp.exe
long handle = Long.parseLong(wp.hWndStr);
if (handle == 0)
return;
System.out.println("calling getRuntimeIdFromHandle: " + handle);
String rid = wb.getRuntimeIdFromHandle(handle);
System.out.println(wp.hWndStr + " getRuntimeIdFromHandle: " + rid);
assertTrue(rid != null);
String[] props = wb.getPropertiesAndValues(rid);
assertTrue(props != null);
//for(String p : props)
// System.out.println(p);
}
@Test
public void enumerateWindowInfo() {
WpfBridge wb = new WpfBridge();
Api api = new Api();
wb.setFrameworkId("WPF");
wb.setTouchableOnly(false);
WinPtr wp = new WinPtr(api.user32.FindWindow(null, "MainWindow"));//find WpfMockTestApp.exe
long handle = Long.parseLong(wp.hWndStr);
if (handle == 0)
return;
String rid = wb.getRuntimeIdFromHandle(handle);
System.out.println(wp.hWndStr + " getRuntimeIdFromHandle: " + rid);
if (rid == null)
return;
String[] wInfo = wb.enumDescendantWindowInfo(rid, WindowInfo.WPF_PROPERTY_LIST);
System.out.println("enumDescendantWindowInfo length: " + wInfo.length);
System.out.println(WindowInfo.WPF_PROPERTY_LIST);
for(String w : wInfo)
System.out.println(w);
}
}