diff --git a/native/WpfBridge/WpfAutomation.cpp b/native/WpfBridge/WpfAutomation.cpp index 14e27ce..ce97bc1 100644 --- a/native/WpfBridge/WpfAutomation.cpp +++ b/native/WpfBridge/WpfAutomation.cpp @@ -28,12 +28,21 @@ void WpfAutomation::setTouchableOnly(System::Boolean val) System::Windows::Automation::Condition ^ WpfAutomation::getSearchConditions() { - array ^cons = gcnew array(3); - cons[0] = gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId);//is WPF framework - cons[1] = gcnew PropertyCondition(AutomationElement::IsEnabledProperty, this->touchableOnly);//is enabled - cons[2] = gcnew PropertyCondition(AutomationElement::IsOffscreenProperty, !this->touchableOnly);// is off screen - System::Windows::Automation::Condition ^result = gcnew System::Windows::Automation::AndCondition(cons); - return result; + array ^cons = nullptr; + if (this->touchableOnly) + { + cons = gcnew array(3); + cons[0] = gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId);//is WPF framework + cons[1] = gcnew PropertyCondition(AutomationElement::IsEnabledProperty, true);//is enabled + cons[2] = gcnew PropertyCondition(AutomationElement::IsOffscreenProperty, false);// is off screen + } + else + { + cons = gcnew array(1); + cons[0] = gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId);//is WPF framework + } + System::Windows::Automation::Condition ^result = gcnew System::Windows::Automation::AndCondition(cons); + return result; } array ^ WpfAutomation::convertRuntimeIdString(System::String ^runtimeIdValue) @@ -48,11 +57,13 @@ array ^ WpfAutomation::convertRuntimeIdString(System::String ^run return idArray; } -AutomationElement ^ WpfAutomation::findAutomationElementById(System::String ^runtimeIdValue) +AutomationElement ^ WpfAutomation::findAutomationElementById(System::String ^runtimeIdValue, System::Boolean unfiltered) { if (runtimeIdValue == nullptr || runtimeIdValue->Equals(L"")) return AutomationElement::RootElement; array ^idArray = this->convertRuntimeIdString(runtimeIdValue); + if (unfiltered) + return AutomationElement::RootElement->FindFirst(TreeScope::Descendants, gcnew PropertyCondition(AutomationElement::RuntimeIdProperty, idArray)); //Condition ^pcFramework = gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId); Condition ^pcRunId = gcnew PropertyCondition(AutomationElement::RuntimeIdProperty, idArray); Condition ^frameworkAndRuntimeId = gcnew AndCondition(getSearchConditions(), pcRunId); @@ -102,7 +113,7 @@ System::Int32 WpfAutomation::countDescendantWindows() System::Int32 WpfAutomation::countDescendantWindows(System::String ^runtimeIdValue) { - AutomationElement ^parent = findAutomationElementById(runtimeIdValue); + AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true); AutomationElementCollection ^aec = parent->FindAll(TreeScope::Descendants, getSearchConditions()); if (aec == nullptr) return 0; @@ -125,7 +136,7 @@ System::Int32 WpfAutomation::countChildrenWindows() System::Int32 WpfAutomation::countChildrenWindows(System::String ^runtimeIdValue) { - AutomationElement ^parent = findAutomationElementById(runtimeIdValue); + AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true); AutomationElementCollection ^aec = parent->FindAll(TreeScope::Children, getSearchConditions()); if (aec == nullptr) return 0; @@ -137,7 +148,7 @@ System::Int32 WpfAutomation::countChildrenWindows(System::String ^runtimeIdValue array ^ WpfAutomation::enumChildrenWindowIds(System::String ^runtimeIdValue) { - AutomationElement ^parent = findAutomationElementById(runtimeIdValue); + AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true); AutomationElementCollection ^aec = parent->FindAll(TreeScope::Children, getSearchConditions()); if (aec == nullptr) return nullptr; @@ -146,7 +157,7 @@ array ^ WpfAutomation::enumChildrenWindowIds(System::String ^r array ^ WpfAutomation::enumDescendantWindowIds(System::String ^runtimeIdValue) { - AutomationElement ^parent = findAutomationElementById(runtimeIdValue); + AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true); AutomationElementCollection ^aec = parent->FindAll(TreeScope::Descendants, getSearchConditions()); if (aec == nullptr) return nullptr; @@ -176,7 +187,7 @@ System::String ^ WpfAutomation::getRuntimeIdFromHandle(System::IntPtr windowHand array ^ WpfAutomation::enumDescendantWindowInfo(System::String ^runtimeIdValue, System::String ^properties) { - AutomationElement ^parent = findAutomationElementById(runtimeIdValue); + AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true); if (parent == nullptr) return nullptr; AutomationElementCollection ^aec = parent->FindAll(TreeScope::Descendants, getSearchConditions()); @@ -226,6 +237,7 @@ array ^ WpfAutomation::enumDescendantWindowInfo(System::String else//not runtimeId which is an Int32[] { currentPropertyStr = currentVal->ToString(); + currentPropertyStr = currentPropertyStr->Replace(",",","); } } if (currentPropertyStr->Equals(L"")) //if there isn't a value skip @@ -256,7 +268,7 @@ System::String ^ WpfAutomation::getRuntimeIdFromPoint(System::Int32 x, System::I System::String ^ WpfAutomation::getParentRuntimeId(System::String ^runtimeIdValue) { - AutomationElement ^target = findAutomationElementById(runtimeIdValue); + AutomationElement ^target = findAutomationElementById(runtimeIdValue, true); if (target == nullptr) return nullptr; TreeWalker ^tw = TreeWalker::ControlViewWalker; @@ -266,7 +278,7 @@ System::String ^ WpfAutomation::getParentRuntimeId(System::String ^runtimeIdValu System::String ^ WpfAutomation::getProperty(System::String ^propertyName, System::String ^runtimeIdValue) { - AutomationElement ^parent = findAutomationElementById(runtimeIdValue); + AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true); if (parent == nullptr) return nullptr; //System::Object ^currentVal = parent->GetCurrentPropertyValue(AutomationElement::RuntimeIdProperty); @@ -286,7 +298,7 @@ System::String ^ WpfAutomation::getProperty(System::String ^propertyName, System array ^ WpfAutomation::getProperties(System::String ^runtimeIdValue) { - AutomationElement ^parent = findAutomationElementById(runtimeIdValue); + AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true); if (parent == nullptr) return nullptr; array ^aps = parent->GetSupportedProperties(); @@ -305,7 +317,7 @@ array ^ WpfAutomation::getProperties(System::String ^runtimeId array ^ WpfAutomation::getPropertiesAndValues(System::String ^runtimeIdValue) { - AutomationElement ^parent = findAutomationElementById(runtimeIdValue); + AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true); if (parent == nullptr) return nullptr; array ^aps = parent->GetSupportedProperties(); diff --git a/native/WpfBridge/WpfAutomation.h b/native/WpfBridge/WpfAutomation.h index a22d6bc..fe8adb9 100644 --- a/native/WpfBridge/WpfAutomation.h +++ b/native/WpfBridge/WpfAutomation.h @@ -34,7 +34,7 @@ public: array ^ getPropertiesAndValues(System::String ^runtimeIdValue); private: array ^ convertRuntimeIdString(System::String ^runtimeIdValue); - System::Windows::Automation::AutomationElement ^ findAutomationElementById(System::String ^runtimeIdValue); + System::Windows::Automation::AutomationElement ^ findAutomationElementById(System::String ^runtimeIdValue, System::Boolean unfiltered); System::String ^ getRuntimeIdFromElement(System::Windows::Automation::AutomationElement ^element); array ^ getRuntimeIdsFromCollection(System::Windows::Automation::AutomationElementCollection ^collection); System::Windows::Automation::Condition ^ getSearchConditions(); diff --git a/native/WpfBridge/bin/wpfbridge32.dll b/native/WpfBridge/bin/wpfbridge32.dll index e7732cc..4dd0ad1 100644 Binary files a/native/WpfBridge/bin/wpfbridge32.dll and b/native/WpfBridge/bin/wpfbridge32.dll differ diff --git a/native/WpfBridge/bin/wpfbridge64.dll b/native/WpfBridge/bin/wpfbridge64.dll index fa640a9..4570fa3 100644 Binary files a/native/WpfBridge/bin/wpfbridge64.dll and b/native/WpfBridge/bin/wpfbridge64.dll differ diff --git a/src/org/synthuse/Config.java b/src/org/synthuse/Config.java index 8440901..6886a8e 100644 --- a/src/org/synthuse/Config.java +++ b/src/org/synthuse/Config.java @@ -12,6 +12,7 @@ public class Config extends PropertiesSerializer { public static String DEFAULT_PROP_FILENAME = "synthuse.properties"; public String disableWpf = "false"; + public String disableFiltersWpf = "false"; public String urlList = ""; public String xpathList = ""; public String xpathHightlight = ".*process=\"([^\"]*)\".*"; @@ -28,6 +29,15 @@ public class Config extends PropertiesSerializer { public boolean isWpfBridgeDisabled() { + if (disableWpf == null) + return false; return disableWpf.equals("true") || disableWpf.equals("True"); } + + public boolean isFilterWpfDisabled() + { + if (disableFiltersWpf == null) + return false; + return disableFiltersWpf.equals("true") || disableFiltersWpf.equals("True"); + } } diff --git a/src/org/synthuse/WindowInfo.java b/src/org/synthuse/WindowInfo.java index a2b5e2f..53d2861 100644 --- a/src/org/synthuse/WindowInfo.java +++ b/src/org/synthuse/WindowInfo.java @@ -17,7 +17,7 @@ import com.sun.jna.ptr.PointerByReference; public class WindowInfo { - public static String WPF_PROPERTY_LIST = "RuntimeIdProperty,ParentRuntimeIdProperty,ProcessIdProperty,FrameworkIdProperty,ClassNameProperty,NameProperty"; + public static String WPF_PROPERTY_LIST = "RuntimeIdProperty,ParentRuntimeIdProperty,ProcessIdProperty,FrameworkIdProperty,ClassNameProperty,NameProperty,ValueProperty"; public HWND hwnd; public String hwndStr = ""; @@ -25,6 +25,7 @@ public class WindowInfo { public String parentStr = ""; public RECT rect; public String text; + public String value; public String className = ""; public boolean isChild = false; public String processName = ""; @@ -69,6 +70,18 @@ public class WindowInfo { hwndStr = Api.GetHandleAsString(hWnd); } + public String replaceEscapedCodes(String input) { + //, is a comma , + String result = input; + result = result.replaceAll(",", ","); + result = result.replaceAll("<", "<"); + result = result.replaceAll(">", ">"); + result = result.replaceAll("'", "'"); + result = result.replaceAll(""", "\""); + result = result.replaceAll("&", "&"); + return result; + } + //support for WPF and Silverlight public WindowInfo(String enumProperties, boolean isChild) { //WPF_PROPERTY_LIST = "RuntimeIdProperty,ParentRuntimeIdProperty,ProcessIdProperty,FrameworkIdProperty,ClassNameProperty,NameProperty"; @@ -84,9 +97,11 @@ public class WindowInfo { if (spltProperties.length > 3) this.framework = spltProperties[3]; if (spltProperties.length > 4) - this.className = spltProperties[4]; + this.className = replaceEscapedCodes(spltProperties[4]); if (spltProperties.length > 5) - this.text = spltProperties[5]; + this.text = replaceEscapedCodes(spltProperties[5]); + if (spltProperties.length > 6) + this.value = replaceEscapedCodes(spltProperties[6]); /* this.rect = new RECT(); try { diff --git a/src/org/synthuse/WindowsEnumeratedXml.java b/src/org/synthuse/WindowsEnumeratedXml.java index 647f8be..92b0dfa 100644 --- a/src/org/synthuse/WindowsEnumeratedXml.java +++ b/src/org/synthuse/WindowsEnumeratedXml.java @@ -72,9 +72,11 @@ public class WindowsEnumeratedXml implements Runnable{ public static String getXml() { final Map infoList = new LinkedHashMap(); - final Map processList = new LinkedHashMap(); - final Map wpfParentList = new LinkedHashMap(); + final Map processList = new LinkedHashMap(); + final List wpfParentList = new ArrayList();//HwndWrapper + final List silverlightParentList = new ArrayList();//MicrosoftSilverlight int wpfCount = 0; + int silverlightCount = 0; class ChildWindowCallback implements WinUser.WNDENUMPROC { @Override @@ -82,7 +84,9 @@ public class WindowsEnumeratedXml implements Runnable{ WindowInfo wi = new WindowInfo(hWnd, true); infoList.put(wi.hwndStr, wi); if (wi.className.startsWith("HwndWrapper")) - wpfParentList.put(wi.hwndStr, null); + wpfParentList.add(wi.hwndStr); + if (wi.className.startsWith("MicrosoftSilverlight") || wi.className.startsWith("GeckoPluginWindow")) + silverlightParentList.add(wi.hwndStr); return true; } } @@ -93,7 +97,7 @@ public class WindowsEnumeratedXml implements Runnable{ WindowInfo wi = new WindowInfo(hWnd, false); infoList.put(wi.hwndStr, wi); if (wi.className.startsWith("HwndWrapper")) - wpfParentList.put(wi.hwndStr, null); + wpfParentList.add(wi.hwndStr); Api.User32.instance.EnumChildWindows(hWnd, new ChildWindowCallback(), new Pointer(0)); return true; } @@ -103,16 +107,16 @@ public class WindowsEnumeratedXml implements Runnable{ //Enumerate WPF windows and add to list if (!SynthuseDlg.config.isWpfBridgeDisabled()) { - ////win[starts-with(@class,'HwndWrapper')] - //WpfBridge wb = new WpfBridge(); - //if (wb.countDescendantWindows() > 0) - for (String handle : wpfParentList.keySet()) { - //WindowInfo w = wpfParentList.get(handle); - //System.out.println("EnumerateWindowsWithWpfBridge: " + handle); + for (String handle : wpfParentList) { Map wpfInfoList = EnumerateWindowsWithWpfBridge(handle, "WPF"); wpfCount += wpfInfoList.size(); infoList.putAll(wpfInfoList); } + for (String handle : silverlightParentList) { + Map slInfoList = EnumerateWindowsWithWpfBridge(handle, "Silverlight"); + silverlightCount += slInfoList.size(); + infoList.putAll(slInfoList); + } } // convert window info list to xml dom @@ -138,6 +142,8 @@ public class WindowsEnumeratedXml implements Runnable{ win = doc.createElement("wpf"); win.setAttribute("hwnd", w.hwndStr); win.setAttribute("text", w.text); + if (w.value != "" && w.value != null) + win.setAttribute("value", w.value); if (w.text != null) win.setAttribute("TEXT", w.text.toUpperCase()); win.setAttribute("class", w.className); @@ -179,6 +185,7 @@ public class WindowsEnumeratedXml implements Runnable{ totals.setAttribute("windowCount", infoList.size()+""); totals.setAttribute("wpfWrapperCount", wpfParentList.size()+""); totals.setAttribute("wpfCount", wpfCount+""); + totals.setAttribute("silverlightCount", silverlightCount+""); totals.setAttribute("processCount", processList.size()+""); totals.setAttribute("updatedLast", new Timestamp((new Date()).getTime()) + ""); rootElement.appendChild(totals); @@ -196,6 +203,8 @@ public class WindowsEnumeratedXml implements Runnable{ final Map infoList = new LinkedHashMap(); WpfBridge wb = new WpfBridge(); wb.setFrameworkId(frameworkType); + if (SynthuseDlg.config.isFilterWpfDisabled()) + wb.setTouchableOnly(false); long hwnd = Long.parseLong(parentHwndStr); //List parentIds = new ArrayList(Arrays.asList(wb.enumChildrenWindowIds(""))); //System.out.println("getRuntimeIdFromHandle"); diff --git a/src/org/synthuse/WpfBridge.java b/src/org/synthuse/WpfBridge.java index c76cf1c..3e26bfd 100644 --- a/src/org/synthuse/WpfBridge.java +++ b/src/org/synthuse/WpfBridge.java @@ -114,7 +114,11 @@ public class WpfBridge { 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); } diff --git a/src/org/synthuse/XpathManager.java b/src/org/synthuse/XpathManager.java index 2c11563..29418e3 100644 --- a/src/org/synthuse/XpathManager.java +++ b/src/org/synthuse/XpathManager.java @@ -84,18 +84,29 @@ public class XpathManager implements Runnable{ 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) return ""; //System.out.println("text: " + txtOrig); String txtStr = compareLongTextString(txtOrig); - builtXpath = "//wpf[@class='" + classStr + "' and starts-with(@text,'" + txtStr + "')]"; + + String valueStr = ""; + if (winValueOrig != null) + if (!winValueOrig.isEmpty()) //if value attribute exists then use it too + valueStr = " and starts-with(@value,'" + compareLongTextString(winValueOrig) + "')"; + + builtXpath = "//wpf[@class='" + classStr + "' and starts-with(@text,'" + txtStr + "')" + valueStr + "]"; + //builtXpath = "//*[@hwnd='" + runtimeId + "']"; - System.out.println("evaluateXpathGetValues: " + builtXpath); + //System.out.println("evaluateXpathGetValues: " + builtXpath); List wpfResultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath); - if (wpfResultList.size() == 0) - return ""; - // return builtXpath; - return builtXpath; + if (wpfResultList.size() > 0) + return builtXpath; + builtXpath = "//*[@hwnd='" + runtimeId + "']"; + wpfResultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath); + if (wpfResultList.size() > 0) + return builtXpath; + return ""; } public String buildXpathStatement() { diff --git a/src/org/synthuse/test/WpfBridgeTest.java b/src/org/synthuse/test/WpfBridgeTest.java index 317da61..f02f5d0 100644 --- a/src/org/synthuse/test/WpfBridgeTest.java +++ b/src/org/synthuse/test/WpfBridgeTest.java @@ -19,41 +19,86 @@ public class WpfBridgeTest { } @Test - public void countChildren() { + public void countChildrenWin32() { WpfBridge wb = new WpfBridge(); wb.setFrameworkId("Win32");//We should find some Win32 windows, maybe not WPF int win32Cnt = wb.countChildrenWindows(); - assertTrue(win32Cnt > 0); System.out.println("win32 countChildrenWindows: " + win32Cnt); - wb.setFrameworkId("WPF");// maybe not WPF - //System.out.println("wpf countChildrenWindows: " + wb.countChildrenWindows()); + 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() { - long handle = 1639790; - //String rid = wb.getRuntimeIdFromHandle(handle); - //System.out.println("getRuntimeIdFromHandle: " + rid); - handle = 984416; - //rid = wb.getRuntimeIdFromHandle(handle); - //System.out.println("getRuntimeIdFromHandle: " + rid); + 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); + 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(); - /* - EnumerateWindowsWithWpfBridge: 1639790 - getRuntimeIdFromHandle - runtimeId=42-1639790 - enumDescendantWindowIds 18 - EnumerateWindowsWithWpfBridge: 984416 - getRuntimeIdFromHandle - runtimeId=42-984416 - */ - //int count = wb.countDescendantWindows("42-984416"); - String[] wInfo = wb.enumDescendantWindowInfo("42-984416", WindowInfo.WPF_PROPERTY_LIST); - if (wInfo != null) - System.out.println("enumDescendantWindowInfo: " + wInfo.length); + 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); + 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); } } diff --git a/synthuse.properties b/synthuse.properties index 6bbd54a..734eda2 100644 --- a/synthuse.properties +++ b/synthuse.properties @@ -1,7 +1,8 @@ # -#Sat Apr 05 17:18:52 EDT 2014 +#Tue Apr 08 22:36:47 EDT 2014 DEFAULT_PROP_FILENAME= urlList= +disableFiltersWpf=false xpathList= xpathHightlight=.*process\="([^"]*)".* disableWpf=false