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:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
//, is a comma ,
|
||||
String result = input;
|
||||
result = result.replaceAll(",", ",");
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user