391 lines
14 KiB
Java
391 lines
14 KiB
Java
/*
|
|
* Copyright 2014, Synthuse.org
|
|
* Released under the Apache Version 2.0 License.
|
|
*
|
|
* last modified by ejakubowski
|
|
*/
|
|
|
|
package org.synthuse;
|
|
|
|
import java.io.StringReader;
|
|
import java.io.StringWriter;
|
|
import java.sql.Timestamp;
|
|
import java.text.DecimalFormat;
|
|
import java.util.ArrayList;
|
|
import java.util.Date;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
import javax.swing.JLabel;
|
|
import javax.swing.JTextPane;
|
|
import javax.xml.parsers.DocumentBuilder;
|
|
import javax.xml.parsers.DocumentBuilderFactory;
|
|
import javax.xml.transform.OutputKeys;
|
|
import javax.xml.transform.Transformer;
|
|
import javax.xml.transform.TransformerFactory;
|
|
import javax.xml.transform.dom.DOMSource;
|
|
import javax.xml.transform.stream.StreamResult;
|
|
import javax.xml.xpath.XPath;
|
|
import javax.xml.xpath.XPathConstants;
|
|
import javax.xml.xpath.XPathExpression;
|
|
import javax.xml.xpath.XPathFactory;
|
|
|
|
import org.w3c.dom.Attr;
|
|
import org.w3c.dom.Document;
|
|
import org.w3c.dom.Element;
|
|
import org.w3c.dom.Node;
|
|
import org.w3c.dom.NodeList;
|
|
import org.xml.sax.InputSource;
|
|
|
|
import com.sun.jna.Pointer;
|
|
import com.sun.jna.platform.win32.WinDef.HMENU;
|
|
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;
|
|
public JLabel lblStatus = null;
|
|
public WindowsEnumeratedXml() {
|
|
}
|
|
|
|
public WindowsEnumeratedXml(JTextPane outputPane, JLabel lblStatus) {
|
|
this.outputPane = outputPane;
|
|
this.lblStatus = lblStatus;
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
lblStatus.setText("Loading Windows Enumerated Xml...");
|
|
long startTime = System.nanoTime();
|
|
outputPane.setText(getXml());
|
|
outputPane.setCaretPosition(0);
|
|
double seconds = ((double)(System.nanoTime() - startTime) / 1000000000);
|
|
lblStatus.setText("Windows Enumerated Xml loaded in " + new DecimalFormat("#.###").format(seconds) + " seconds");
|
|
enumeratingXmlFlag.set(false);
|
|
}
|
|
|
|
public static void getXmlThreaded(JTextPane outputPane, JLabel lblStatus) {
|
|
if (enumeratingXmlFlag.get())
|
|
return; //something is already running
|
|
enumeratingXmlFlag.set(true); //if we don't do this the multiple xml's could get combined on the textpane
|
|
Thread t = new Thread(new WindowsEnumeratedXml(outputPane, lblStatus));
|
|
t.start();
|
|
}
|
|
|
|
public static String getXml() {
|
|
final Map<String, WindowInfo> infoList = new LinkedHashMap<String, WindowInfo>();
|
|
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) {
|
|
WindowInfo wi = new WindowInfo(hWnd, true);
|
|
infoList.put(wi.hwndStr, wi);
|
|
if (wi.className.startsWith("HwndWrapper"))
|
|
wpfParentList.add(wi.hwndStr);
|
|
if (wi.className.startsWith("MicrosoftSilverlight") || wi.className.startsWith("GeckoPluginWindow"))
|
|
silverlightParentList.add(wi.hwndStr);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class ParentWindowCallback implements WinUser.WNDENUMPROC {
|
|
@Override
|
|
public boolean callback(HWND hWnd, Pointer lParam) {
|
|
WindowInfo wi = new WindowInfo(hWnd, false);
|
|
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, WinForm, Silverlight windows and add to list
|
|
if (!SynthuseDlg.config.isUiaBridgeDisabled())
|
|
{
|
|
UiaBridge uiabridge = new UiaBridge();
|
|
for (String handle : wpfParentList) {
|
|
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 = EnumerateWindowsWithUiaBridge(uiabridge, handle, "Silverlight");
|
|
silverlightCount += slInfoList.size();
|
|
infoList.putAll(slInfoList);
|
|
}
|
|
}
|
|
|
|
// convert window info list to xml dom
|
|
try {
|
|
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
|
|
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
|
|
|
|
// root elements
|
|
Document doc = docBuilder.newDocument();
|
|
Element rootElement = doc.createElement("EnumeratedWindows");
|
|
doc.appendChild(rootElement);
|
|
|
|
long parentCount = 0;
|
|
long childCount = 0;
|
|
for (String handle : infoList.keySet()) {
|
|
WindowInfo w = infoList.get(handle);
|
|
//System.out.println(w);
|
|
// create new win xml element
|
|
Element win = null;
|
|
if (w.framework.equals("win32"))
|
|
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);
|
|
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);
|
|
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()) {
|
|
if (!processList.containsKey(w.pid+""))
|
|
processList.put(w.pid+"", w.hwndStr);
|
|
win.setAttribute("process", w.processName);
|
|
if (w.processName != null)
|
|
win.setAttribute("PROCESS", w.processName.toUpperCase());
|
|
}
|
|
}
|
|
if (w.pid != 0)
|
|
win.setAttribute("pid", w.pid+"");
|
|
//else
|
|
//win.setAttribute("parent", w.parent + ""); // not really needed since child node is append to parent node
|
|
if (w.extra != null) {
|
|
for(String extraName: w.extra.keySet()) {
|
|
win.setAttribute(extraName, w.extra.get(extraName)+"");
|
|
}
|
|
}
|
|
|
|
if (w.menus > 0) {
|
|
win.setAttribute("menus", w.menus+"");
|
|
//String menuStr = MenuInfo.GetHandleMenuAsString(w.menu);
|
|
buildMenuXmlElements(doc, win, w.menu, w.hwndStr);
|
|
++menuCount;
|
|
}
|
|
|
|
if (w.isChild && infoList.containsKey(w.parentStr)) {
|
|
childCount++;
|
|
WindowInfo parentWi = infoList.get(w.parentStr);
|
|
if (parentWi.xmlObj != null)
|
|
((Element)parentWi.xmlObj).appendChild(win);
|
|
else
|
|
rootElement.appendChild(win);
|
|
}
|
|
else
|
|
rootElement.appendChild(win);
|
|
w.xmlObj = win;
|
|
|
|
}
|
|
|
|
// calculate totals on various windows.
|
|
Element totals = doc.createElement("totals");
|
|
totals.setAttribute("parentCount", parentCount+"");
|
|
totals.setAttribute("childCount", childCount+"");
|
|
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()+"");
|
|
totals.setAttribute("updatedLast", new Timestamp((new Date()).getTime()) + "");
|
|
rootElement.appendChild(totals);
|
|
String output = nodeToString(rootElement);
|
|
//System.out.println("count - " + infoList.size() + "\r\n");
|
|
return output;
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
lastException = e;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
public static Element buildMenuXmlElements(Document xmlDoc, Element xmlElement, HMENU targetMenu, String targetWin)
|
|
{
|
|
MenuInfo firstMi = new MenuInfo(targetWin, targetMenu);
|
|
for (int i = 0 ; i < firstMi.menuCount ; i++ ) {
|
|
MenuInfo menuInfo = new MenuInfo(targetWin, targetMenu, i);
|
|
Element menuElement = xmlDoc.createElement("menu");
|
|
menuElement.setAttribute("unaltered", menuInfo.unaltered + "");
|
|
menuElement.setAttribute("text", menuInfo.text + "");
|
|
menuElement.setAttribute("id", menuInfo.id + "");
|
|
menuElement.setAttribute("position", menuInfo.position + "");
|
|
menuElement.setAttribute("hmenu", menuInfo.hmenuStr + "");
|
|
menuElement.setAttribute("hwnd", menuInfo.hwndStr + "");
|
|
if (!menuInfo.center.isEmpty())
|
|
menuElement.setAttribute("center", menuInfo.center + "");
|
|
if (menuInfo.hasSubMenu) {
|
|
buildMenuXmlElements(xmlDoc, menuElement, menuInfo.submenu, targetWin);
|
|
}
|
|
xmlElement.appendChild(menuElement);
|
|
}
|
|
return xmlElement;
|
|
}
|
|
|
|
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();
|
|
//wpf.setFrameworkId(frameworkType);
|
|
long hwnd = Long.parseLong(parentHwndStr);
|
|
//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.isFilterUiaDisabled())
|
|
allIds = uiabridge.enumWindowInfo((int) hwnd, "*");
|
|
else
|
|
allIds = uiabridge.enumWindowInfo((int) hwnd, WindowInfo.UIA_PROPERTY_LIST);
|
|
if (allIds == null)
|
|
return infoList; //empty list
|
|
//System.out.println("enumDescendantWindowIds " + allIds.length);
|
|
for(String runtimeIdAndInfo : allIds) {
|
|
//System.out.println("getting window info for: " + runtimeIdAndInfo);
|
|
WindowInfo wi = new WindowInfo(runtimeIdAndInfo, true);
|
|
if (wi.parentStr.equals(parentRuntimeId))
|
|
wi.parentStr = parentHwndStr;
|
|
//System.out.println("is parent? " + onlyRuntimeId);
|
|
infoList.put(wi.runtimeId, wi);
|
|
}
|
|
return infoList;
|
|
}
|
|
|
|
public static String escapeXmlAttributeValue(String unescapedStr) {
|
|
String result = "";
|
|
try {
|
|
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
|
|
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
|
|
// root elements
|
|
Document doc = docBuilder.newDocument();
|
|
Element rootElement = doc.createElement("temp");
|
|
doc.appendChild(rootElement);
|
|
Attr attribute = doc.createAttribute("attrib");
|
|
attribute.setNodeValue(unescapedStr);
|
|
rootElement.setAttributeNode(attribute);
|
|
result = nodeToString(rootElement);
|
|
result = result.replaceAll("[^\"]*\"([^\"]*)\"[^\"]*", "$1"); // keep the string within quotes.
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
//lastException = e;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static String nodeToString(Node node) {
|
|
StringWriter sw = new StringWriter();
|
|
try {
|
|
Transformer t = TransformerFactory.newInstance().newTransformer();
|
|
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
|
|
t.setOutputProperty(OutputKeys.INDENT, "yes");
|
|
t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
|
|
t.transform(new DOMSource(node), new StreamResult(sw));
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
lastException = e;
|
|
}
|
|
return sw.toString();
|
|
}
|
|
|
|
public static String queryWindowInfoXml(String xpathExpr) {
|
|
String nl = System.getProperty("line.separator");
|
|
String result = "<result>" + nl;
|
|
String xml = getXml();
|
|
List<String> resultList = evaluateXpathGetValues(xml, xpathExpr);
|
|
for(String item: resultList) {
|
|
result += " " + item;
|
|
}
|
|
result += "</result>" + nl;
|
|
return result;
|
|
}
|
|
|
|
public static HWND queryHandleWindowInfoXml(String xpathExpr) {
|
|
String xml = getXml();
|
|
String result = "";
|
|
List<String> resultList = evaluateXpathGetValues(xml, xpathExpr);
|
|
for(String item: resultList) {
|
|
if (item.contains("hwnd")) {
|
|
List<String> hwndList = evaluateXpathGetValues(item, "//@hwnd");
|
|
result = hwndList.get(0);
|
|
}
|
|
else
|
|
result = item;
|
|
break;
|
|
}
|
|
return Api.GetHandleFromString(result);
|
|
}
|
|
|
|
|
|
public static List<String> evaluateXpathGetValues(String xml, String xpathExpr) {
|
|
List<String> resultLst = new ArrayList<String>();
|
|
try {
|
|
InputSource inSource = new InputSource(new StringReader(xml));
|
|
XPathFactory factory = XPathFactory.newInstance();
|
|
XPath xpath = factory.newXPath();
|
|
XPathExpression expr = xpath.compile(xpathExpr);
|
|
|
|
Object result = expr.evaluate(inSource, XPathConstants.NODESET);
|
|
NodeList nodes = (NodeList) result;
|
|
for (int i = 0; i < nodes.getLength(); i++) {
|
|
String val = nodes.item(i).getNodeValue();
|
|
if (val == null) // if we can't get a string value try to transform the xml node to a string
|
|
val = nodeToString(nodes.item(i));
|
|
else
|
|
val += System.getProperty("line.separator");
|
|
resultLst.add(val);
|
|
//System.out.println("match " + i + ": " + val);
|
|
}
|
|
} catch(Exception e) {
|
|
e.printStackTrace();
|
|
lastException = e;
|
|
}
|
|
return resultLst;
|
|
}
|
|
|
|
}
|