Added support for scripting menus, and showing counts for lists, comboboxes, treeviews, listviews

Support for scripting menus and seeing more information about various
list types, and partially working toolbars
This commit is contained in:
Edward Jakubowski
2014-04-19 21:17:35 -04:00
parent dcae627527
commit b5082e2b22
15 changed files with 541 additions and 87 deletions

View File

@@ -188,10 +188,15 @@ System::String ^ WpfAutomation::getRuntimeIdFromHandle(System::IntPtr windowHand
array<System::String ^> ^ WpfAutomation::enumDescendantWindowInfo(System::String ^runtimeIdValue, System::String ^properties) array<System::String ^> ^ WpfAutomation::enumDescendantWindowInfo(System::String ^runtimeIdValue, System::String ^properties)
{ {
AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true); AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true);
if (parent == nullptr) if (parent == nullptr)
return nullptr; return nullptr;
AutomationElementCollection ^aec = parent->FindAll(TreeScope::Descendants, getSearchConditions()); AutomationElementCollection ^aec = parent->FindAll(TreeScope::Descendants, getSearchConditions());
//when wildcard is enabled it will pull all property names & values
System::Boolean wildcardEnabled = false;
if (properties->Equals(L"*"))
wildcardEnabled = true;
//create array for keeping order of properties //create array for keeping order of properties
System::String ^delim = L","; System::String ^delim = L",";
array<System::String ^> ^propSpltArray = properties->Split(delim->ToCharArray()); array<System::String ^> ^propSpltArray = properties->Split(delim->ToCharArray());
@@ -203,6 +208,11 @@ array<System::String ^> ^ WpfAutomation::enumDescendantWindowInfo(System::String
{ {
array<AutomationProperty^> ^aps = child->GetSupportedProperties(); array<AutomationProperty^> ^aps = child->GetSupportedProperties();
array<System::String ^> ^propValues = gcnew array<System::String ^>(propSpltArray->Length);//keep order array<System::String ^> ^propValues = gcnew array<System::String ^>(propSpltArray->Length);//keep order
System::String ^wildcardProperties = L"";
if (wildcardEnabled) {
wildcardProperties += "ParentRuntimeIdProperty:" + getRuntimeIdFromElement(tw->GetParent(child)) + ",";
//propValues = gcnew array<System::String ^>(aps->Length +1 );//add one for parent property since it doesn't exist
}
for(int i=0 ; i < propValues->Length ; i++) for(int i=0 ; i < propValues->Length ; i++)
{ {
propValues[i] = L""; propValues[i] = L"";
@@ -218,7 +228,7 @@ array<System::String ^> ^ WpfAutomation::enumDescendantWindowInfo(System::String
System::String ^shortPropName = L" null "; System::String ^shortPropName = L" null ";
if (ap->ProgrammaticName->Contains(L".")) if (ap->ProgrammaticName->Contains(L"."))
shortPropName = ap->ProgrammaticName->Substring(ap->ProgrammaticName->IndexOf(L".") + 1); shortPropName = ap->ProgrammaticName->Substring(ap->ProgrammaticName->IndexOf(L".") + 1);
if (properties->Contains(shortPropName) || properties->Contains(ap->ProgrammaticName) || ap->ProgrammaticName->Equals(properties)) if (properties->Contains(shortPropName) || properties->Contains(ap->ProgrammaticName) || ap->ProgrammaticName->Equals(properties) || wildcardEnabled)
{ {
//System::Console::WriteLine("shortPropName: {0}", shortPropName); //System::Console::WriteLine("shortPropName: {0}", shortPropName);
System::Object ^currentVal = child->GetCurrentPropertyValue(ap); System::Object ^currentVal = child->GetCurrentPropertyValue(ap);
@@ -242,17 +252,23 @@ array<System::String ^> ^ WpfAutomation::enumDescendantWindowInfo(System::String
} }
if (currentPropertyStr->Equals(L"")) //if there isn't a value skip if (currentPropertyStr->Equals(L"")) //if there isn't a value skip
continue; continue;
if (wildcardEnabled) {
wildcardProperties += shortPropName + ":" +currentPropertyStr + ",";
continue;
}
//System::Console::WriteLine("currentPropertyStr: {0}", currentPropertyStr); //System::Console::WriteLine("currentPropertyStr: {0}", currentPropertyStr);
//find the correct order to return this property //find the correct order to return this property
for(int i=0 ; i < propSpltArray->Length ; i++) for(int i=0 ; i < propSpltArray->Length ; i++)
{ {
if (propSpltArray[i]->Equals(shortPropName) || propSpltArray[i]->Equals(ap->ProgrammaticName)) if (propSpltArray[i]->Equals(shortPropName) || propSpltArray[i]->Equals(ap->ProgrammaticName))
propValues[i] = currentPropertyStr; propValues[i] = currentPropertyStr;
} }
} }
//output properties in the correct order //output properties in the correct order
for(int i=0 ; i < propSpltArray->Length ; i++) for(int i=0 ; i < propSpltArray->Length ; i++)
winInfoList[count] += propValues[i] + L","; winInfoList[count] += propValues[i] + L",";
if (wildcardEnabled)
winInfoList[count] += wildcardProperties;
++count; ++count;
} }
return winInfoList; return winInfoList;

Binary file not shown.

Binary file not shown.

View File

@@ -8,18 +8,17 @@
package org.synthuse; package org.synthuse;
import java.awt.Point; import java.awt.Point;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Native; import com.sun.jna.Native;
import com.sun.jna.Pointer; import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinDef.HDC; import com.sun.jna.Structure;
import com.sun.jna.platform.win32.WinDef.HPEN; import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.platform.win32.WinUser; import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.platform.win32.WinNT.LARGE_INTEGER; import com.sun.jna.platform.win32.WinNT.LARGE_INTEGER;
import com.sun.jna.platform.win32.WinUser.WNDENUMPROC; import com.sun.jna.platform.win32.WinUser.WNDENUMPROC;
import com.sun.jna.ptr.PointerByReference; import com.sun.jna.ptr.PointerByReference;
@@ -27,6 +26,8 @@ import com.sun.jna.win32.W32APIOptions;
public class Api { public class Api {
// Constants
public static int WM_SETTEXT = 0x000c; public static int WM_SETTEXT = 0x000c;
public static int WM_GETTEXT = 0x000D; public static int WM_GETTEXT = 0x000D;
public static int WM_GETTEXTLENGTH = 0x000E; public static int WM_GETTEXTLENGTH = 0x000E;
@@ -84,6 +85,24 @@ public class Api {
public static int RDW_INVALIDATE = 0x0001; public static int RDW_INVALIDATE = 0x0001;
public static int RDW_UPDATENOW = 0x0100; public static int RDW_UPDATENOW = 0x0100;
public static int RDW_ALLCHILDREN = 0x0080; public static int RDW_ALLCHILDREN = 0x0080;
public static int TB_GETBUTTONTEXTA = (0x0400 + 45);
public static int TB_GETBUTTONTEXTW = (0x0400 + 75);
public static int TB_GETRECT = (0x0400 + 51);
public static int TB_GETTOOLTIPS = (0x0400 + 35);
public static int TB_BUTTONCOUNT = 0x0418;
public static int LVM_FIRST = 0x1000;
public static int LVM_GETITEMCOUNT = LVM_FIRST + 4;
public static int LVM_GETITEM = LVM_FIRST + 75;
public static int LVIF_TEXT = 0x0001;
public static int LB_GETCOUNT = 0x18B;
public static int CB_GETCOUNT = 0x146;
public static int TV_FIRST = 0x1100;
public static int TVM_GETCOUNT = TV_FIRST + 5;
public static int VK_SHIFT = 16; public static int VK_SHIFT = 16;
public static int VK_LSHIFT = 0xA0; public static int VK_LSHIFT = 0xA0;
@@ -95,6 +114,8 @@ public class Api {
public static int VK_LMENU = 0xA4; public static int VK_LMENU = 0xA4;
public static int VK_RMENU = 0xA5; public static int VK_RMENU = 0xA5;
public static int WM_COMMAND = 0x111;
public static int CWP_ALL = 0x0000; // Does not skip any child windows public static int CWP_ALL = 0x0000; // Does not skip any child windows
public User32 user32; public User32 user32;
@@ -109,7 +130,60 @@ public class Api {
public static final int POINT_X(long i) public static final int POINT_X(long i)
{ {
return (int) (i & 0xFFFF); return (int) (i & 0xFFFF);
} }
public interface WinDefExt extends WinDef {
//Structures
public class MENUITEMINFO extends Structure {
public static final int MFS_CHECKED = 0x00000008;
public static final int MFS_DEFAULT = 0x00001000;
public static final int MFS_DISABLED = 0x00000003;
public static final int MFS_ENABLED = 0x00000000;
public static final int MFS_GRAYED = 0x00000003;
public static final int MFS_HILITE = 0x00000080;
public static final int MFS_UNCHECKED = 0x00000000;
public static final int MFS_UNHILITE = 0x00000000;
public static final int MFT_STRING = 0x0000;
public static final int MIIM_DATA = 0x00000020;
public static final int MIIM_STRING = 0x0040;
public static final int MIIM_SUBMENU = 0x0004;
public static final int MIIM_TYPE = 0x0010;
public static class ByValue extends MENUITEMINFO implements Structure.ByValue {
}
public static class ByReference extends MENUITEMINFO implements Structure.ByReference {
}
public MENUITEMINFO() {
cbSize = size();
}
public MENUITEMINFO(Pointer p) {
super(p);
}
@Override
protected List<?> getFieldOrder() {
return Arrays.asList(new String[] { "cbSize", "fMask", "fType", "fState", "wID", "hSubMenu", "hbmpChecked",
"hbmpUnchecked", "dwItemData", "dwTypeData", "cch", "hbmpItem" });
}
public int cbSize; //The size of the structure, in bytes. The caller must set this member to sizeof(MENUITEMINFO).
public int fMask; //Indicates the members to be retrieved or set. MIIM_STRING or MIIM_SUBMENU or ...
public int fType; //The menu item type. fType is used only if fMask has a value of MIIM_FTYPE.
public int fState; //The menu item state. This member can be one or more of these values. Set fMask to MIIM_STATE to use fState.
public int wID; //An application-defined value that identifies the menu item. Set fMask to MIIM_ID to use wID.
public HMENU hSubMenu; //A handle to the drop-down menu or submenu associated with the menu item. Or NULL
public HBITMAP hbmpChecked; //A handle to the bitmap to display next to the item if it is selected.
public HBITMAP hbmpUnchecked; //A handle to the bitmap to display next to the item if it is not selected.
public ULONG_PTR dwItemData; //An application-defined value associated with the menu item. Set fMask to MIIM_DATA
//public byte[] dwTypeData = new byte[256];
public String dwTypeData; //The contents of the menu item, depends on the value of fType and is used only if the MIIM_TYPE flag is set in the fMask member
public int cch; //The length of the menu item text, in characters, when information is received about a menu item of the MFT_STRING type.
public HBITMAP hbmpItem; //A handle to the bitmap to be displayed, or it can be one of the values in the following table.
}
}
public interface User32 extends W32APIOptions { public interface User32 extends W32APIOptions {
User32 instance = (User32) Native.loadLibrary("user32", User32.class, DEFAULT_OPTIONS); User32 instance = (User32) Native.loadLibrary("user32", User32.class, DEFAULT_OPTIONS);
@@ -158,6 +232,20 @@ public class Api {
boolean ScreenToClient(HWND hWnd, long[] lpPoint);//use macros POINT_X() and POINT_Y() on long lpPoint[0] boolean ScreenToClient(HWND hWnd, long[] lpPoint);//use macros POINT_X() and POINT_Y() on long lpPoint[0]
//HWND WindowFromPoint(int xPoint, int yPoint); //HWND WindowFromPoint(int xPoint, int yPoint);
//HWND WindowFromPoint(POINT point); //HWND WindowFromPoint(POINT point);
HMENU GetMenu(HWND hWnd);
boolean IsMenu(HMENU hMenu);
int GetMenuString(HMENU hMenu, int uIDItem, char[] buffer, int nMaxCount, int uFlag);
HMENU GetSubMenu(HMENU hMenu, int nPos);
int GetMenuItemCount(HMENU hMenu);
int GetMenuItemID(HMENU hMenu, int nPos);
//BOOL WINAPI GetMenuItemInfo(_In_ HMENU hMenu, _In_ UINT uItem, _In_ BOOL fByPosition, _Inout_ LPMENUITEMINFO lpmii);
boolean GetMenuItemInfoA(HMENU hMenu, int uItem, boolean fByPosition, WinDefExt.MENUITEMINFO mii); //MENUITEMINFO
boolean TrackPopupMenu(HMENU hMenu, int uFlags, int x, int y, int nReserved, HWND hWnd, long prcRect);
//
int GetDlgCtrlID(HWND hwndCtl);
int GetDlgItemText(HWND hDlg, int nIDDlgItem, byte[] buffer, int nMaxCount);
} }
public interface Gdi32 extends W32APIOptions { public interface Gdi32 extends W32APIOptions {
@@ -346,6 +434,32 @@ public class Api {
user32.SendMessageA(handle, WM_KEYUP, keyCode, null); user32.SendMessageA(handle, WM_KEYUP, keyCode, null);
} }
public String GetMenuItemText(HMENU hmenu, int position) {
if (user32.IsMenu(hmenu) == false)
return "";
char[] buffer = new char[256];
user32.GetMenuString(hmenu, position, buffer, 256, 0x0400);
return Native.toString(buffer);
/*
Api.WinDefExt.MENUITEMINFO mii = new Api.WinDefExt.MENUITEMINFO(); // = (MENUITEMINFO)Api.MENUITEMINFO.newInstance(Api.MENUITEMINFO.class);
mii.fMask = Api.WinDefExt.MENUITEMINFO.MIIM_TYPE;
mii.fType = Api.WinDefExt.MENUITEMINFO.MFT_STRING;
mii.cch = 0;
mii.dwTypeData = "";
@SuppressWarnings("unused")
boolean result = Api.User32.instance.GetMenuItemInfoA(hmenu, position, true, mii);
//System.out.println(position + " GetMenuItemInfo (" + result + ") : " + mii.cch + " " + mii.dwTypeData);
mii.fMask = Api.WinDefExt.MENUITEMINFO.MIIM_TYPE;
mii.fType = Api.WinDefExt.MENUITEMINFO.MFT_STRING;
mii.cch += 1;
mii.dwTypeData = "";//new String(new char[mii.cch]).replace("\0", " "); //buffer string with spaces
result = Api.User32.instance.GetMenuItemInfoA(hmenu, position, true, mii);
//System.out.println(position + " GetMenuItemInfo2 (" + result + ") Text: " + mii.dwTypeData + " " + mii.cch + " " + mii.wID);
//System.out.println("last error: " + Api.Kernel32.instance.GetLastError());
return mii.dwTypeData;
*/
}
public Point getWindowPosition(HWND handle) { public Point getWindowPosition(HWND handle) {
Point windowPoint = new Point(); Point windowPoint = new Point();
RECT rect = new RECT(); RECT rect = new RECT();

View File

@@ -106,6 +106,9 @@ public class CommandPopupMenu extends JPopupMenu {
CommandMenuItem mntmWindowSwitchToThis = new CommandMenuItem("windowSwitchToThis", 2); CommandMenuItem mntmWindowSwitchToThis = new CommandMenuItem("windowSwitchToThis", 2);
mnWinMessages.add(mntmWindowSwitchToThis); mnWinMessages.add(mntmWindowSwitchToThis);
CommandMenuItem mntmSelectMenu = new CommandMenuItem("selectMenu", 2);
mnWinMessages.add(mntmSelectMenu);
CommandMenuItem mntmSetcursorposition = new CommandMenuItem("setCursorPosition", 3); CommandMenuItem mntmSetcursorposition = new CommandMenuItem("setCursorPosition", 3);
mnWinMessages.add(mntmSetcursorposition); mnWinMessages.add(mntmSetcursorposition);

View File

@@ -203,6 +203,8 @@ public class CommandProcessor implements Runnable{
//Windows Api Commands //Windows Api Commands
if (command.equals("windowFocus")) if (command.equals("windowFocus"))
return win.cmdWindowFocus(args); return win.cmdWindowFocus(args);
if (command.equals("selectMenu"))
return win.cmdSelectMenu(args);
if (command.equals("windowMinimize")) if (command.equals("windowMinimize"))
return win.cmdWindowMinimize(args); return win.cmdWindowMinimize(args);
if (command.equals("windowMaximize")) if (command.equals("windowMaximize"))

View File

@@ -0,0 +1,85 @@
package org.synthuse;
import java.math.BigInteger;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinDef.HMENU;
public class MenuInfo {
public HMENU hmenu = null;
public String hmenuStr = "";
public String hwndStr = "";
public int menuCount = 0;
public String text = "";
public String unaltered = "";
public int id = 0;
public int position = 0;
public boolean hasSubMenu = false;
public HMENU submenu = null;
public String submenuStr = "";
public int submenuCount = 0;
public MenuInfo(HMENU hmenu) {
loadMenuBase(hmenu);
}
public MenuInfo(HMENU hmenu, int position) {
loadMenuBase(hmenu);
if (this.menuCount > 0)
loadMenuDetails(hmenu, position);
}
public void loadMenuBase(HMENU hmenu) {
Api api = new Api();
this.hmenu = hmenu;
this.hmenuStr = GetHandleMenuAsString(hmenu);
this.menuCount = api.user32.GetMenuItemCount(hmenu);
}
public void loadMenuDetails(HMENU hmenu, int position) {
Api api = new Api();
this.position = position;
this.unaltered = api.GetMenuItemText(hmenu, position);
this.text = unaltered;
if (this.unaltered.contains("\t"))
this.text = this.text.substring(0, this.text.indexOf("\t"));
this.text = text.replaceAll("[^a-zA-Z0-9.,\\+ ]", "");
this.id = api.user32.GetMenuItemID(hmenu, position);
HMENU submenu = api.user32.GetSubMenu(hmenu, position);
if (submenu != null) {
int subCount = api.user32.GetMenuItemCount(submenu);
if (subCount > 0) {
this.hasSubMenu = true;
this.submenu = submenu;
this.submenuStr = GetHandleMenuAsString(submenu);
this.submenuCount = subCount;
}
}
}
public static String GetHandleMenuAsString(HMENU hmenu) {
if (hmenu == null)
return "0";
//String longHexStr = hWnd.toString().substring("native@".length());
//String longHexStr = hmenu.getPointer()
String longHexStr = hmenu.getPointer().toString().substring("native@0x".length());
long l = new BigInteger(longHexStr, 16).longValue();
return l + "";
}
public static HMENU GetHandleMenuFromString(String hmenu) {
if (hmenu == null)
return null;
if (hmenu.isEmpty())
return null;
String cleanNumericHandle = hmenu.replaceAll("[^\\d.]", "");
try {
return (new HMENU(new Pointer(Long.parseLong(cleanNumericHandle))));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

View File

@@ -7,12 +7,19 @@
package org.synthuse; package org.synthuse;
import java.util.LinkedHashMap;
import java.util.Map;
import org.synthuse.Api.*; import org.synthuse.Api.*;
import com.sun.jna.Native; import com.sun.jna.Native;
import com.sun.jna.Pointer; import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinDef.HMENU;
import com.sun.jna.platform.win32.WinDef.HWND; import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.RECT; import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.ptr.PointerByReference; import com.sun.jna.ptr.PointerByReference;
public class WindowInfo { public class WindowInfo {
@@ -33,6 +40,10 @@ public class WindowInfo {
public Object xmlObj = null; public Object xmlObj = null;
public String framework = "win32";//default as win32 public String framework = "win32";//default as win32
public String runtimeId = ""; public String runtimeId = "";
public int menus = 0;
public HMENU menu = null;
public Map<String, String> extra = null;
//Default Win32 support //Default Win32 support
public WindowInfo(HWND hWnd, boolean isChild) { public WindowInfo(HWND hWnd, boolean isChild) {
@@ -42,7 +53,68 @@ public class WindowInfo {
text = Native.toString(buffer); text = Native.toString(buffer);
if (text.isEmpty()) if (text.isEmpty())
text = new Api().sendWmGetText(hWnd); text = new Api().sendWmGetText(hWnd);
if (text.isEmpty()) {
//System.out.println("getting toolbar text");
}
//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));
if (tbCount.intValue() > 0) { // toolbar button count
//System.out.println("TB_BUTTONCOUNT: " + tbCount.intValue());
if (extra == null)
extra = new LinkedHashMap<String, String>();
extra.put("tbCount", tbCount.intValue() + "");
//Api.User32.instance.SendMessageA(hWnd, Api.TB_GETTOOLTIPS, 0, buffer);
//text = Native.toString(buffer);
}
LRESULT lvCount = Api.User32.instance.SendMessage(hWnd, Api.LVM_GETITEMCOUNT, new WPARAM(0), new LPARAM(0));
if (lvCount.intValue() > 0) { // listview item count
if (extra == null)
extra = new LinkedHashMap<String, String>();
extra.put("lvCount", lvCount.intValue() + "");
}
LRESULT lbCount = Api.User32.instance.SendMessage(hWnd, Api.LB_GETCOUNT, new WPARAM(0), new LPARAM(0));
if (lbCount.intValue() > 0) { // listbox item count
if (extra == null)
extra = new LinkedHashMap<String, String>();
extra.put("lbCount", lbCount.intValue() + "");
}
LRESULT cbCount = Api.User32.instance.SendMessage(hWnd, Api.CB_GETCOUNT, new WPARAM(0), new LPARAM(0));
if (cbCount.intValue() > 0) { // listbox item count
if (extra == null)
extra = new LinkedHashMap<String, String>();
extra.put("cbCount", cbCount.intValue() + "");
}
LRESULT tvCount = Api.User32.instance.SendMessage(hWnd, Api.TVM_GETCOUNT, new WPARAM(0), new LPARAM(0));
if (tvCount.intValue() > 0) { //treeview node count
if (extra == null)
extra = new LinkedHashMap<String, String>();
extra.put("tvCount", tvCount.intValue() + "");
}
//check if window has a menu
HMENU hmenu = Api.User32.instance.GetMenu(hWnd);
if (hmenu != null) { //menu item count
int menuCount = Api.User32.instance.GetMenuItemCount(hmenu);
if (menuCount > 0) {
this.menus = menuCount;
this.menu = hmenu;
}
}
if (isChild) {
int ctrlID = Api.User32.instance.GetDlgCtrlID(hWnd);
if (ctrlID > 0){
//parent = User32.instance.GetParent(hWnd);
int dtresult = Api.User32.instance.GetDlgItemText(hWnd, ctrlID, buffer, 1024);
if (dtresult > 0) {
String dgText = Native.toString(buffer);
if (extra == null)
extra = new LinkedHashMap<String, String>();
extra.put("dgText", dgText + "");
}
}
}
char[] buffer2 = new char[1026]; char[] buffer2 = new char[1026];
User32.instance.GetClassName(hWnd, buffer2, 1026); User32.instance.GetClassName(hWnd, buffer2, 1026);
className = Native.toString(buffer2); className = Native.toString(buffer2);
@@ -85,12 +157,41 @@ public class WindowInfo {
public WindowInfo(String enumProperties, boolean isChild) { public WindowInfo(String enumProperties, boolean isChild) {
//WPF_PROPERTY_LIST = "RuntimeIdProperty,ParentRuntimeIdProperty,ProcessIdProperty,FrameworkIdProperty,ClassNameProperty,NameProperty"; //WPF_PROPERTY_LIST = "RuntimeIdProperty,ParentRuntimeIdProperty,ProcessIdProperty,FrameworkIdProperty,ClassNameProperty,NameProperty";
String[] spltProperties = enumProperties.split(","); String[] spltProperties = enumProperties.split(",");
this.isChild = isChild;
if (SynthuseDlg.config.isFilterWpfDisabled()) { //use wildcard mode
extra = new LinkedHashMap<String, String>();
for(String prop: spltProperties) {
String[] propertyNameAndValue = prop.split(":", 2);
if (propertyNameAndValue.length < 2)
continue;
if (propertyNameAndValue[0].equals("RuntimeIdProperty"))
this.runtimeId = propertyNameAndValue[1];
else if (propertyNameAndValue[0].equals("ParentRuntimeIdProperty"))
this.parentStr = propertyNameAndValue[1];
else if (propertyNameAndValue[0].equals("ProcessIdProperty"))
this.pid = Long.parseLong(propertyNameAndValue[1]);
else if (propertyNameAndValue[0].equals("FrameworkIdProperty"))
this.framework = propertyNameAndValue[1];
else if (propertyNameAndValue[0].equals("ClassNameProperty"))
this.className = propertyNameAndValue[1];
else if (propertyNameAndValue[0].equals("NameProperty"))
this.text = propertyNameAndValue[1];
else if (propertyNameAndValue[0].equals("ValueProperty"))
this.value = propertyNameAndValue[1];
else{
extra.put(propertyNameAndValue[0], propertyNameAndValue[1]);
}
}
this.hwndStr = this.runtimeId;
return;
}
// non-wildcard mode
if (spltProperties.length > 0) if (spltProperties.length > 0)
this.runtimeId = spltProperties[0]; this.runtimeId = spltProperties[0];
this.hwndStr = this.runtimeId; this.hwndStr = this.runtimeId;
if (spltProperties.length > 1 && isChild) if (spltProperties.length > 1 && isChild)
this.parentStr = spltProperties[1]; this.parentStr = spltProperties[1];
this.isChild = isChild;
if (spltProperties.length > 2) if (spltProperties.length > 2)
this.pid = Long.parseLong(spltProperties[2]); this.pid = Long.parseLong(spltProperties[2]);
if (spltProperties.length > 3) if (spltProperties.length > 3)

View File

@@ -39,6 +39,7 @@ import org.w3c.dom.NodeList;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import com.sun.jna.Pointer; 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.WinUser;
import com.sun.jna.platform.win32.WinDef.HWND; import com.sun.jna.platform.win32.WinDef.HWND;
@@ -77,6 +78,7 @@ public class WindowsEnumeratedXml implements Runnable{
final List<String> silverlightParentList = new ArrayList<String>();//MicrosoftSilverlight final List<String> silverlightParentList = new ArrayList<String>();//MicrosoftSilverlight
int wpfCount = 0; int wpfCount = 0;
int silverlightCount = 0; int silverlightCount = 0;
int menuCount = 0;
class ChildWindowCallback implements WinUser.WNDENUMPROC { class ChildWindowCallback implements WinUser.WNDENUMPROC {
@Override @Override
@@ -142,6 +144,7 @@ public class WindowsEnumeratedXml implements Runnable{
win = doc.createElement("wpf"); win = doc.createElement("wpf");
else if (w.framework.equals("Silverlight")) else if (w.framework.equals("Silverlight"))
win = doc.createElement("silver"); win = doc.createElement("silver");
win.setAttribute("hwnd", w.hwndStr); win.setAttribute("hwnd", w.hwndStr);
win.setAttribute("text", w.text); win.setAttribute("text", w.text);
if (w.value != "" && w.value != null) if (w.value != "" && w.value != null)
@@ -165,6 +168,18 @@ public class WindowsEnumeratedXml implements Runnable{
win.setAttribute("pid", w.pid+""); win.setAttribute("pid", w.pid+"");
//else //else
//win.setAttribute("parent", w.parent + ""); // not really needed since child node is append to parent node //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)) { if (w.isChild && infoList.containsKey(w.parentStr)) {
childCount++; childCount++;
@@ -188,6 +203,7 @@ public class WindowsEnumeratedXml implements Runnable{
totals.setAttribute("wpfWrapperCount", wpfParentList.size()+""); totals.setAttribute("wpfWrapperCount", wpfParentList.size()+"");
totals.setAttribute("wpfCount", wpfCount+""); totals.setAttribute("wpfCount", wpfCount+"");
totals.setAttribute("silverlightCount", silverlightCount+""); totals.setAttribute("silverlightCount", silverlightCount+"");
totals.setAttribute("menuCount", menuCount+"");
totals.setAttribute("processCount", processList.size()+""); totals.setAttribute("processCount", processList.size()+"");
totals.setAttribute("updatedLast", new Timestamp((new Date()).getTime()) + ""); totals.setAttribute("updatedLast", new Timestamp((new Date()).getTime()) + "");
rootElement.appendChild(totals); rootElement.appendChild(totals);
@@ -201,6 +217,26 @@ public class WindowsEnumeratedXml implements Runnable{
return ""; return "";
} }
public static Element buildMenuXmlElements(Document xmlDoc, Element xmlElement, HMENU targetMenu, String targetWin)
{
MenuInfo firstMi = new MenuInfo(targetMenu);
for (int i = 0 ; i < firstMi.menuCount ; i++ ) {
MenuInfo menuInfo = new MenuInfo(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", targetWin + "");
if (menuInfo.hasSubMenu) {
buildMenuXmlElements(xmlDoc, menuElement, menuInfo.submenu, targetWin);
}
xmlElement.appendChild(menuElement);
}
return xmlElement;
}
public static Map<String, WindowInfo> EnumerateWindowsWithWpfBridge(String parentHwndStr, String frameworkType) { public static Map<String, WindowInfo> EnumerateWindowsWithWpfBridge(String parentHwndStr, String frameworkType) {
final Map<String, WindowInfo> infoList = new LinkedHashMap<String, WindowInfo>(); final Map<String, WindowInfo> infoList = new LinkedHashMap<String, WindowInfo>();
WpfBridge wb = new WpfBridge(); WpfBridge wb = new WpfBridge();
@@ -212,7 +248,11 @@ public class WindowsEnumeratedXml implements Runnable{
//System.out.println("getRuntimeIdFromHandle"); //System.out.println("getRuntimeIdFromHandle");
String parentRuntimeId = wb.getRuntimeIdFromHandle(hwnd); String parentRuntimeId = wb.getRuntimeIdFromHandle(hwnd);
//System.out.println("runtimeId=" + runtimeId); //System.out.println("runtimeId=" + runtimeId);
String[] allIds = wb.enumDescendantWindowInfo(parentRuntimeId, WindowInfo.WPF_PROPERTY_LIST); String[] allIds = null;
if (SynthuseDlg.config.isFilterWpfDisabled())
allIds = wb.enumDescendantWindowInfo(parentRuntimeId, "*");
else
allIds = wb.enumDescendantWindowInfo(parentRuntimeId, WindowInfo.WPF_PROPERTY_LIST);
if (allIds == null) if (allIds == null)
return infoList; //empty list return infoList; //empty list
//System.out.println("enumDescendantWindowIds " + allIds.length); //System.out.println("enumDescendantWindowIds " + allIds.length);

View File

@@ -100,7 +100,7 @@ public class XpathManager implements Runnable{
//builtXpath = "//*[@hwnd='" + runtimeId + "']"; //builtXpath = "//*[@hwnd='" + runtimeId + "']";
//System.out.println("evaluateXpathGetValues: " + builtXpath); //System.out.println("evaluateXpathGetValues: " + builtXpath);
List<String> wpfResultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath); List<String> wpfResultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath);
if (wpfResultList.size() > 0) if (wpfResultList.size() == 1)
return builtXpath; return builtXpath;
builtXpath = "//*[@hwnd='" + runtimeId + "']"; builtXpath = "//*[@hwnd='" + runtimeId + "']";
wpfResultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath); wpfResultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath);
@@ -181,7 +181,12 @@ public class XpathManager implements Runnable{
if (targetText instanceof JTextPane) { if (targetText instanceof JTextPane) {
final JTextPane target = (JTextPane)targetText; final JTextPane target = (JTextPane)targetText;
target.requestFocus(); target.requestFocus();
int cPos = target.getCaretPosition(); int cPos = 0;
try {
cPos = target.getCaretPosition();
} catch(Exception ex) {
//return 0;//something is throwing nullpointer exception
}
if (alwaysFromTop) if (alwaysFromTop)
cPos = 0; cPos = 0;
int len = target.getStyledDocument().getLength(); int len = target.getStyledDocument().getLength();

View File

@@ -129,6 +129,36 @@ public class BaseCommand {
return result; return result;
} }
public int findMenuIdWithXpath(String xpath) {
int result = 0;
double secondsFromLastUpdate = ((double)(System.nanoTime() - LAST_UPDATED_XML) / 1000000000);
if (secondsFromLastUpdate > CommandProcessor.XML_UPDATE_THRESHOLD) { //default 5 second threshold
WIN_XML = WindowsEnumeratedXml.getXml();
LAST_UPDATED_XML = System.nanoTime();
}
WindowsEnumeratedXml.evaluateXpathGetValues(WIN_XML, xpath);
String resultStr = "";
List<String> resultList = WindowsEnumeratedXml.evaluateXpathGetValues(WIN_XML, xpath);
for(String item: resultList) {
if (item.contains("hmenu=")) {
List<String> list = WindowsEnumeratedXml.evaluateXpathGetValues(item, "//@id");
if (list.size() > 0)
resultStr = list.get(0); //get first id;
}
else
resultStr = item;
break;
}
resultStr = resultStr.replaceAll("[^\\d.]", ""); //remove all non-numeric values
//System.out.println("findMenuIdWithXpath: " + resultStr);
if (resultStr.isEmpty())
appendError("Error: Failed to find window handle matching: " + xpath);
else
result = Integer.parseInt(resultStr);
return result;
}
public Point getCenterWindowPosition(WinPtr handle) { public Point getCenterWindowPosition(WinPtr handle) {
Point p = null; Point p = null;
if (handle.isWin32()) if (handle.isWin32())

View File

@@ -2,6 +2,9 @@ package org.synthuse.commands;
import org.synthuse.*; import org.synthuse.*;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.WPARAM;
public class WindowsCommands extends BaseCommand { public class WindowsCommands extends BaseCommand {
public WindowsCommands(CommandProcessor cp) { public WindowsCommands(CommandProcessor cp) {
@@ -108,4 +111,17 @@ public class WindowsCommands extends BaseCommand {
return ""; return "";
return api.sendWmGetText(handle.hWnd); return api.sendWmGetText(handle.hWnd);
} }
public boolean cmdSelectMenu(String[] args) {
if (!checkArgumentLength(args, 1))
return false;
WinPtr handle = findHandleWithXpath(args[0]);
if (handle.isEmpty())
return false;
int id = findMenuIdWithXpath(args[0]);
//LRESULT result =
//System.out.println("PostMessage to " + handle.hWndStr + " for id " + id);
api.user32.PostMessage(handle.hWnd, Api.WM_COMMAND, new WPARAM(id), new LPARAM(0));
return true;
}
} }

View File

@@ -0,0 +1,79 @@
package org.synthuse.test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
// This class doesn't contain Test, only some methods that are used by the unit tests
public class UnitTestHelper {
static Process runningApp = null;
public static void RunApp(String ResourceFilePath) {
String tempFilename = ExtractFileFromJar(ResourceFilePath);
Runtime runtime = Runtime.getRuntime();
try {
runningApp = runtime.exec(tempFilename);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void DestroyApp() {
if (runningApp != null)
runningApp.destroy();
}
public static String ExtractFileFromJar(String path) {
// Obtain filename from path
String[] parts = path.split("/");
String filename = (parts.length > 1) ? parts[parts.length - 1] : null;
// Split filename to prexif and suffix (extension)
String prefix = "";
String suffix = null;
if (filename != null) {
parts = filename.split("\\.", 2);
prefix = parts[0];
suffix = (parts.length > 1) ? "."+parts[parts.length - 1] : null;
}
File temp = null;
try {
// Prepare temporary file
temp = File.createTempFile(prefix, suffix);
temp.deleteOnExit();
} catch(Exception e) {
e.printStackTrace();
}
if (!temp.exists()) { //some reason the temp file wasn't create so abort
System.out.println("File " + temp.getAbsolutePath() + " does not exist.");
return null;
}
// Prepare buffer for data copying
byte[] buffer = new byte[1024];
int readBytes;
// Open and check input stream
InputStream is = WpfBridgeTest.class.getResourceAsStream(path);
if (is == null) { //check if valid
System.out.println("File " + path + " was not found inside JAR.");
return null;
}
// Open output stream and copy data between source file in JAR and the temporary file
OutputStream os = null;
try {
os = new FileOutputStream(temp);
while ((readBytes = is.read(buffer)) != -1) {
os.write(buffer, 0, readBytes);
}
os.close();
is.close();
} catch(Exception e) {
e.printStackTrace();
}
return temp.getAbsolutePath();
// Finally, load the library
//System.load(temp.getAbsolutePath());
}
}

View File

@@ -20,11 +20,13 @@ import org.synthuse.Api.User32;
import com.sun.jna.Native; import com.sun.jna.Native;
import com.sun.jna.Pointer; import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinUser; import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinDef.HMENU;
import com.sun.jna.platform.win32.WinDef.HWND; import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.RECT; import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.ptr.PointerByReference; import com.sun.jna.ptr.PointerByReference;
public class WinApiTest { public class WinApiTest {
Api api = new Api();
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
@@ -37,7 +39,7 @@ public class WinApiTest {
} }
public static void output(String val) { public static void output(String val) {
System.out.println(val); //System.out.println(val);
} }
//copied and modified slightly from WindowInfo class //copied and modified slightly from WindowInfo class
@@ -50,6 +52,8 @@ public class WinApiTest {
String className = ""; String className = "";
String processName = ""; String processName = "";
long pid = 0; long pid = 0;
Map<String, String> extra = null;
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
output("Calling GetWindowTextA"); output("Calling GetWindowTextA");
@@ -68,6 +72,32 @@ public class WinApiTest {
className = Native.toString(buffer2); className = Native.toString(buffer2);
output("GetClassName returned: " + className); output("GetClassName returned: " + className);
HMENU hmenu = Api.User32.instance.GetMenu(hWnd);
//hmenu = Api.User32.instance.GetSubMenu(hmenu, 0);
if (hmenu != null) { //menu item count
int menuCount = Api.User32.instance.GetMenuItemCount(hmenu);
if (menuCount > 0) {
if (extra == null)
extra = new LinkedHashMap<String, String>();
extra.put("menuCount", menuCount + "");
System.out.println("className: " + className);
System.out.println("text: " + text);
System.out.println("menuCount: " + menuCount);
for (int m = 0 ; m < menuCount ; m++) {
String menuTxt = api.GetMenuItemText(hmenu, m);
System.out.println("Menu Text: " + menuTxt);
}
/*
if (menuCount == 5) {
HMENU smenu = Api.User32.instance.GetSubMenu(hmenu, 0);
boolean result = Api.User32.instance.TrackPopupMenu(smenu, 0, 1, 1, 0, hWnd, 0);
System.out.println("TrackPopupMenu: " + result);
System.out.println("last error: " + Api.Kernel32.instance.GetLastError());
}*/
}
}
rect = new RECT(); rect = new RECT();
output("Calling GetWindowRect"); output("Calling GetWindowRect");
User32.instance.GetWindowRect(hWnd, rect); User32.instance.GetWindowRect(hWnd, rect);

View File

@@ -1,12 +1,6 @@
package org.synthuse.test; package org.synthuse.test;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.junit.After; import org.junit.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
@@ -16,28 +10,17 @@ import org.synthuse.*;
public class WpfBridgeTest { public class WpfBridgeTest {
static Process mockApp = null;
public static void RunMockWpfApp() {
String tempFilename = ExtractFileFromJar("/org/synthuse/test/WpfMockTestApp.exe");
Runtime runtime = Runtime.getRuntime();
try {
mockApp = runtime.exec(tempFilename);
} catch (IOException e) {
e.printStackTrace();
}
}
@BeforeClass @BeforeClass
public static void setUpBeforeClass() { public static void setUpBeforeClass() {
//this runs only once for this class //this runs only once for this class
RunMockWpfApp(); UnitTestHelper.RunApp("/org/synthuse/test/WpfMockTestApp.exe");
} }
@AfterClass @AfterClass
public static void tearDownAfterClass() { public static void tearDownAfterClass() {
//this runs only once for this class //this runs only once for this class
if (mockApp != null) UnitTestHelper.DestroyApp();
mockApp.destroy();
} }
@Before @Before
@@ -139,54 +122,4 @@ public class WpfBridgeTest {
System.out.println(w); System.out.println(w);
} }
public static String ExtractFileFromJar(String path) {
// Obtain filename from path
String[] parts = path.split("/");
String filename = (parts.length > 1) ? parts[parts.length - 1] : null;
// Split filename to prexif and suffix (extension)
String prefix = "";
String suffix = null;
if (filename != null) {
parts = filename.split("\\.", 2);
prefix = parts[0];
suffix = (parts.length > 1) ? "."+parts[parts.length - 1] : null;
}
File temp = null;
try {
// Prepare temporary file
temp = File.createTempFile(prefix, suffix);
temp.deleteOnExit();
} catch(Exception e) {
e.printStackTrace();
}
if (!temp.exists()) { //some reason the temp file wasn't create so abort
System.out.println("File " + temp.getAbsolutePath() + " does not exist.");
return null;
}
// Prepare buffer for data copying
byte[] buffer = new byte[1024];
int readBytes;
// Open and check input stream
InputStream is = WpfBridgeTest.class.getResourceAsStream(path);
if (is == null) { //check if valid
System.out.println("File " + path + " was not found inside JAR.");
return null;
}
// Open output stream and copy data between source file in JAR and the temporary file
OutputStream os = null;
try {
os = new FileOutputStream(temp);
while ((readBytes = is.read(buffer)) != -1) {
os.write(buffer, 0, readBytes);
}
os.close();
is.close();
} catch(Exception e) {
e.printStackTrace();
}
return temp.getAbsolutePath();
// Finally, load the library
//System.load(temp.getAbsolutePath());
}
} }