From 2389b13eb7cfafef2339e4c174fdb583561b1382 Mon Sep 17 00:00:00 2001 From: Edward Jakubowski Date: Tue, 8 Apr 2014 22:40:43 -0400 Subject: [PATCH] Added Support for Silverlight. Added more unit tests Support for automating Silverlight in firefox and Internet Explorer is now supported, along with WPF and regular Win32. --- native/WpfBridge/WpfAutomation.cpp | 44 ++++++---- native/WpfBridge/WpfAutomation.h | 2 +- native/WpfBridge/bin/wpfbridge32.dll | Bin 78336 -> 78848 bytes native/WpfBridge/bin/wpfbridge64.dll | Bin 92672 -> 93184 bytes src/org/synthuse/Config.java | 10 +++ src/org/synthuse/WindowInfo.java | 21 ++++- src/org/synthuse/WindowsEnumeratedXml.java | 29 ++++--- src/org/synthuse/WpfBridge.java | 6 +- src/org/synthuse/XpathManager.java | 23 ++++-- src/org/synthuse/test/WpfBridgeTest.java | 91 +++++++++++++++------ synthuse.properties | 3 +- 11 files changed, 168 insertions(+), 61 deletions(-) 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 e7732cc7f95fa69cdeb65e17a183307f6e141dd1..4dd0ad1dbf3309c1b5ef5719779fa5bb53b41c17 100644 GIT binary patch delta 23302 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 delta 22590 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

cpXU>ztA13wIn3&e%Sg@#43^P_wX$6kuirm=o|! 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 F`7g5tAc6n@ delta 19207 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

;-ccJbM1_@jIK}*S>dZfzmQ#+q9ycAJ3Hzg-WOJ z-zs8k!DPnj>GzBVTD^82#V1kxcJ33kTBUv%;HUPph`)!t6omwCT{#j$4omtBNCbe!%_ zgniUbf35ZZ`X9XQd~d#VuCMgSU%Bm}|Eh_vTudBUiZ7?*p{KBeQtDvXG_HaDf S`Q}T|@2qEbZf}?7NB##dKv;MH 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