Keyboard hook fixes, added targetRefresh command
Keyboard hook class now only listens for Hot Keys and not a all keys in a global keyboard hook. This should fix the reliability of the keyboard hook. Added a new command targetRefresh which will allow you to refresh the XML on a specified window and not the entire desktop. This will help if you want to speed up the synthuse script by not continuously doing full refreshes of the xml.
This commit is contained in:
@@ -167,6 +167,9 @@ public class CommandPopupMenu extends JPopupMenu {
|
||||
CommandMenuItem mntmSetUpdateThreshold = new CommandMenuItem("setUpdateThreshold", 2, false);
|
||||
add(mntmSetUpdateThreshold);
|
||||
|
||||
CommandMenuItem mntmTargetRefresh = new CommandMenuItem("targetRefresh", 2);
|
||||
add(mntmTargetRefresh);
|
||||
|
||||
CommandMenuItem mntmVerifyElementNotPresent = new CommandMenuItem("verifyElementNotPresent", 2);
|
||||
add(mntmVerifyElementNotPresent);
|
||||
|
||||
|
||||
@@ -263,6 +263,8 @@ public class CommandProcessor implements Runnable{
|
||||
return main.cmdVerifyElementNotPresent(args);
|
||||
if (command.equals("verifyElementPresent"))
|
||||
return main.cmdVerifyElementPresent(args);
|
||||
if (command.equals("targetRefresh"))
|
||||
return main.cmdTargetRefresh(args);
|
||||
if (command.equals("waitForTitle"))
|
||||
return main.cmdWaitForTitle(args);
|
||||
if (command.equals("waitForText"))
|
||||
|
||||
@@ -15,6 +15,8 @@ import java.util.List;
|
||||
|
||||
import com.sun.jna.*;
|
||||
import com.sun.jna.platform.win32.User32;
|
||||
import com.sun.jna.platform.win32.WinDef;
|
||||
import com.sun.jna.platform.win32.WinDef.HWND;
|
||||
import com.sun.jna.platform.win32.WinUser.*;
|
||||
import com.sun.jna.platform.win32.WinDef.*;
|
||||
import com.sun.jna.win32.W32APIOptions;
|
||||
@@ -24,6 +26,7 @@ public class KeyboardHook implements Runnable{
|
||||
|
||||
// Keyboard event class, interface, and array list
|
||||
public static class TargetKeyPress {
|
||||
int idNumber;
|
||||
int targetKeyCode;
|
||||
boolean withShift, withCtrl, withAlt;
|
||||
public TargetKeyPress (int targetKeyCode) {
|
||||
@@ -32,6 +35,13 @@ public class KeyboardHook implements Runnable{
|
||||
this.withCtrl = false;
|
||||
this.withAlt = false;
|
||||
}
|
||||
public TargetKeyPress (int idNumber, int targetKeyCode, boolean withShift, boolean withCtrl, boolean withAlt) {
|
||||
this.idNumber = idNumber;
|
||||
this.targetKeyCode = targetKeyCode;
|
||||
this.withShift = withShift;
|
||||
this.withCtrl = withCtrl;
|
||||
this.withAlt = withAlt;
|
||||
}
|
||||
public TargetKeyPress (int targetKeyCode, boolean withShift, boolean withCtrl, boolean withAlt) {
|
||||
this.targetKeyCode = targetKeyCode;
|
||||
this.withShift = withShift;
|
||||
@@ -59,6 +69,15 @@ public class KeyboardHook implements Runnable{
|
||||
public static final int VK_MENU = 0x12;
|
||||
public static final int VK_CAPITAL = 0x14;
|
||||
|
||||
public static final int MOD_ALT = 0x0001;
|
||||
public static final int MOD_CONTROL = 0x0002;
|
||||
public static final int MOD_NOREPEAT = 0x4000;
|
||||
public static final int MOD_SHIFT = 0x0004;
|
||||
public static final int MOD_WIN = 0x0008;
|
||||
|
||||
public static final int QS_HOTKEY = 0x0080;
|
||||
public static final int INFINITE = 0xFFFFFFFF;
|
||||
|
||||
public static HHOOK hHook = null;
|
||||
public static LowLevelKeyboardProc lpfn;
|
||||
public static volatile boolean quit = false;
|
||||
@@ -76,6 +95,17 @@ public class KeyboardHook implements Runnable{
|
||||
short GetKeyState(int nVirtKey);
|
||||
short GetAsyncKeyState(int nVirtKey);
|
||||
|
||||
/*
|
||||
DWORD WINAPI MsgWaitForMultipleObjects(
|
||||
__in DWORD nCount, //The number of object handles in the array pointed to by pHandles.
|
||||
__in const HANDLE *pHandles, //An array of object handles.
|
||||
__in BOOL bWaitAll, //If this parameter is TRUE, the function returns when the states of all objects in the pHandles array have been set to signaled and an input event has been received.
|
||||
__in DWORD dwMilliseconds, //if dwMilliseconds is INFINITE, the function will return only when the specified objects are signaled.
|
||||
__in DWORD dwWakeMask //The input types for which an input event object handle will be added to the array of object handles.
|
||||
);*/
|
||||
int MsgWaitForMultipleObjects(int nCount, Pointer pHandles, boolean bWaitAll, int dwMilliSeconds, int dwWakeMask);
|
||||
boolean RegisterHotKey(Pointer hWnd, int id, int fsModifiers, int vk);
|
||||
|
||||
//public static interface HOOKPROC extends StdCallCallback {
|
||||
// LRESULT callback(int nCode, WPARAM wParam, KBDLLHOOKSTRUCT lParam);
|
||||
//}
|
||||
@@ -89,8 +119,10 @@ public class KeyboardHook implements Runnable{
|
||||
|
||||
// Create Global Windows Keyboard hook and wait until quit == true
|
||||
public void createGlobalKeyboardHook() {
|
||||
|
||||
if (hHook != null)
|
||||
return; //hook already running don't add anymore
|
||||
System.out.println("starting global keyboard hook");
|
||||
HMODULE hMod = Kernel32.instance.GetModuleHandle(null);
|
||||
HOOKPROC lpfn = new HOOKPROC() {
|
||||
@SuppressWarnings("unused")
|
||||
@@ -108,33 +140,49 @@ public class KeyboardHook implements Runnable{
|
||||
hHook = User32.INSTANCE.SetWindowsHookEx(WH_KEYBOARD_LL, lpfn, hMod, 0);
|
||||
if (hHook == null)
|
||||
return;
|
||||
|
||||
//System.out.println("starting message loop");
|
||||
MSG msg = new MSG();
|
||||
int cnt = 0;
|
||||
try {
|
||||
|
||||
while (!quit) {
|
||||
User32.INSTANCE.PeekMessage(msg, null, 0, 0, 1);
|
||||
Thread.sleep(10);
|
||||
++cnt;
|
||||
if (cnt > 500)
|
||||
{
|
||||
cnt = 0;
|
||||
//System.out.println("heartbeat test");
|
||||
if (msg.message == User32.WM_HOTKEY){ // && msg.wParam.intValue() == 1
|
||||
//System.out.println("Hot key pressed!");
|
||||
msg = new MSG(); //must clear msg so it doesn't repeat
|
||||
}
|
||||
Thread.sleep(10);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
/*
|
||||
while (!quit) {
|
||||
// hex arguments: WM_KEYFIRST, WM_KEYLAST
|
||||
int result = User32.INSTANCE.GetMessage(msg, null, 0x100, 0x109);
|
||||
if (result == -1) {
|
||||
break;
|
||||
} else {
|
||||
User32.INSTANCE.TranslateMessage(msg);
|
||||
User32.INSTANCE.DispatchMessage(msg);
|
||||
//System.out.println("message loop stopped");
|
||||
}
|
||||
|
||||
// Create HotKeys Windows hook and wait until quit == true
|
||||
public void createHotKeysHook() {
|
||||
registerAllHotKeys();
|
||||
//User32Ex.instance.MsgWaitForMultipleObjects(0, Pointer.NULL, true, INFINITE, QS_HOTKEY);
|
||||
|
||||
//System.out.println("starting message loop");
|
||||
MSG msg = new MSG();
|
||||
try {
|
||||
|
||||
while (!quit) {
|
||||
User32.INSTANCE.PeekMessage(msg, null, 0, 0, 1);
|
||||
if (msg.message == User32.WM_HOTKEY){ // && msg.wParam.intValue() == 1
|
||||
//System.out.println("Hot key pressed " + msg.wParam);
|
||||
TargetKeyPress target = findTargetKeyPressById(msg.wParam.intValue());
|
||||
if (target != null)
|
||||
events.keyPressed(target);
|
||||
msg = new MSG(); //must clear msg so it doesn't repeat
|
||||
}
|
||||
Thread.sleep(10);
|
||||
}
|
||||
}*/
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
unregisterAllHotKeys();
|
||||
//System.out.println("message loop stopped");
|
||||
}
|
||||
|
||||
@@ -149,11 +197,12 @@ public class KeyboardHook implements Runnable{
|
||||
}
|
||||
|
||||
//stops Keyboard hook and causes the unhook command to be called
|
||||
public static void stopGlobalKeyboardHook() {
|
||||
public static void stopKeyboardHook() {
|
||||
quit = true;
|
||||
}
|
||||
|
||||
// search target keyboard event list for a match and return it otherwise return null if no match
|
||||
|
||||
private TargetKeyPress getTargetKeyPressed(int keyCode) {
|
||||
TargetKeyPress target = null;
|
||||
for (TargetKeyPress tkp : KeyboardHook.targetList) {
|
||||
@@ -170,10 +219,52 @@ public class KeyboardHook implements Runnable{
|
||||
return target;
|
||||
}
|
||||
|
||||
private TargetKeyPress findTargetKeyPressById(int idNumber)
|
||||
{
|
||||
TargetKeyPress target = null;
|
||||
for (TargetKeyPress tkp : KeyboardHook.targetList) {
|
||||
if (tkp.idNumber == idNumber)
|
||||
return tkp;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
// add more target keys to watch for
|
||||
public static void addKeyEvent(int targetKeyCode, boolean withShift, boolean withCtrl, boolean withAlt) {
|
||||
KeyboardHook.targetList.add(new TargetKeyPress(targetKeyCode, withShift, withCtrl, withAlt));
|
||||
KeyboardHook.targetList.add(new TargetKeyPress(KeyboardHook.targetList.size() + 1 , targetKeyCode, withShift, withCtrl, withAlt));
|
||||
}
|
||||
|
||||
private void registerAllHotKeys() // must register hot keys in the same thread that is watching for hotkey messages
|
||||
{
|
||||
for (TargetKeyPress tkp : KeyboardHook.targetList) {
|
||||
//BOOL WINAPI RegisterHotKey(HWND hWnd, int id, UINT fsModifiers, UINT vk);
|
||||
int modifiers = User32.MOD_NOREPEAT;
|
||||
if (tkp.withShift)
|
||||
modifiers = modifiers | User32.MOD_SHIFT;
|
||||
if (tkp.withCtrl)
|
||||
modifiers = modifiers | User32.MOD_CONTROL;
|
||||
if (tkp.withAlt)
|
||||
modifiers = modifiers | User32.MOD_ALT;
|
||||
//System.out.println("RegisterHotKey " + tkp.idNumber + "," + modifiers + ", " + tkp.targetKeyCode);
|
||||
|
||||
if (!User32.INSTANCE.RegisterHotKey(new WinDef.HWND(Pointer.NULL), tkp.idNumber, modifiers, tkp.targetKeyCode))
|
||||
{
|
||||
System.out.println("Couldn't register hotkey " + tkp.targetKeyCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterAllHotKeys() // must register hot keys in the same thread that is watching for hotkey messages
|
||||
{
|
||||
for (TargetKeyPress tkp : KeyboardHook.targetList) {
|
||||
if (!User32.INSTANCE.UnregisterHotKey(Pointer.NULL, tkp.idNumber))
|
||||
{
|
||||
System.out.println("Couldn't unregister hotkey " + tkp.targetKeyCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// add more target keys to watch for
|
||||
public static void addKeyEvent(int targetKeyCode) {
|
||||
KeyboardHook.targetList.add(new TargetKeyPress(targetKeyCode));
|
||||
@@ -181,7 +272,8 @@ public class KeyboardHook implements Runnable{
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
createGlobalKeyboardHook();
|
||||
//createGlobalKeyboardHook();
|
||||
createHotKeysHook();
|
||||
//System.out.println("Unhooking Global Keyboard Hook");
|
||||
unhook();//wait for quit == true then unhook
|
||||
}
|
||||
@@ -193,7 +285,7 @@ public class KeyboardHook implements Runnable{
|
||||
this.events = events;
|
||||
}
|
||||
|
||||
public static void StartGlobalKeyboardHookThreaded(KeyboardEvents events) {
|
||||
public static void StartKeyboardHookThreaded(KeyboardEvents events) {
|
||||
Thread t = new Thread(new KeyboardHook(events));
|
||||
t.start();
|
||||
}
|
||||
|
||||
@@ -393,7 +393,7 @@ public class SynthuseDlg extends JFrame {
|
||||
this.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent arg0) {
|
||||
KeyboardHook.stopGlobalKeyboardHook(); //stop keyboard hook
|
||||
KeyboardHook.stopKeyboardHook(); //stop keyboard hook
|
||||
config.save();
|
||||
SynthuseDlg.this.dispose(); // force app to close
|
||||
}
|
||||
@@ -402,7 +402,7 @@ public class SynthuseDlg extends JFrame {
|
||||
KeyboardHook.addKeyEvent(config.getRefreshKeyCode(), true, true, false);// refresh xml when CTRL+SHIFT+3 is pressed
|
||||
KeyboardHook.addKeyEvent(config.getTargetKeyCode(), true, true, false);// target window when CTRL+SHIFT+~ is pressed
|
||||
//add global hook and event
|
||||
KeyboardHook.StartGlobalKeyboardHookThreaded(new KeyboardHook.KeyboardEvents() {
|
||||
KeyboardHook.StartKeyboardHookThreaded(new KeyboardHook.KeyboardEvents() {
|
||||
@Override
|
||||
public void keyPressed(KeyboardHook.TargetKeyPress target) {
|
||||
//System.out.println("target key pressed " + target.targetKeyCode);
|
||||
|
||||
@@ -79,14 +79,9 @@ public class WindowsEnumeratedXml implements Runnable{
|
||||
|
||||
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
|
||||
@@ -126,22 +121,29 @@ public class WindowsEnumeratedXml implements Runnable{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return generateWindowsXml(infoList, "EnumeratedWindows");
|
||||
}
|
||||
|
||||
public static String generateWindowsXml(Map<String, WindowInfo> infoList, String rootElementName)
|
||||
{
|
||||
final Map<String, String> processList = new LinkedHashMap<String, String>();
|
||||
int wpfCount = 0;
|
||||
int winFormCount = 0;
|
||||
int silverlightCount = 0;
|
||||
int menuCount = 0;
|
||||
// convert window info list to xml dom
|
||||
try {
|
||||
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
|
||||
@@ -149,7 +151,7 @@ public class WindowsEnumeratedXml implements Runnable{
|
||||
|
||||
// root elements
|
||||
Document doc = docBuilder.newDocument();
|
||||
Element rootElement = doc.createElement("EnumeratedWindows");
|
||||
Element rootElement = doc.createElement(rootElementName);
|
||||
doc.appendChild(rootElement);
|
||||
|
||||
long parentCount = 0;
|
||||
@@ -162,11 +164,20 @@ public class WindowsEnumeratedXml implements Runnable{
|
||||
if (w.framework.equals("win32"))
|
||||
win = doc.createElement("win");
|
||||
else if (w.framework.equals("WPF"))
|
||||
{
|
||||
win = doc.createElement("wpf");
|
||||
++wpfCount;
|
||||
}
|
||||
else if (w.framework.equals("WinForm"))
|
||||
{
|
||||
win = doc.createElement("winfrm");
|
||||
++winFormCount;
|
||||
}
|
||||
else if (w.framework.equals("Silverlight"))
|
||||
{
|
||||
win = doc.createElement("silver");
|
||||
++silverlightCount;
|
||||
}
|
||||
else
|
||||
win = doc.createElement("win");
|
||||
//System.out.println(w.toString());
|
||||
@@ -229,7 +240,7 @@ public class WindowsEnumeratedXml implements Runnable{
|
||||
totals.setAttribute("parentCount", parentCount+"");
|
||||
totals.setAttribute("childCount", childCount+"");
|
||||
totals.setAttribute("windowCount", infoList.size()+"");
|
||||
totals.setAttribute("wpfWrapperCount", wpfParentList.size()+"");
|
||||
//totals.setAttribute("wpfWrapperCount", wpfParentList.size()+"");
|
||||
totals.setAttribute("wpfCount", wpfCount+"");
|
||||
totals.setAttribute("winFormCount", winFormCount+"");
|
||||
totals.setAttribute("silverlightCount", silverlightCount+"");
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.io.StringWriter;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.synthuse.*;
|
||||
|
||||
@@ -97,6 +98,48 @@ public class BaseCommand {
|
||||
LAST_UPDATED_XML = System.nanoTime();
|
||||
}
|
||||
|
||||
public void targetXmlRefresh(String xpath) {
|
||||
if (WIN_XML.isEmpty()) //can't target refresh unless there is XML to start with
|
||||
{
|
||||
forceXmlRefresh();
|
||||
return;
|
||||
}
|
||||
//WIN_XML = WindowsEnumeratedXml.getXml();
|
||||
LAST_UPDATED_XML = System.nanoTime();
|
||||
|
||||
String resultStr = "";
|
||||
String resultHwndStr = "";
|
||||
List<String> resultList = WindowsEnumeratedXml.evaluateXpathGetValues(WIN_XML, xpath);
|
||||
for(String item: resultList) {
|
||||
//System.out.println("xpath result item: " + item);
|
||||
resultStr = item;
|
||||
if (item.contains("hwnd=")) {
|
||||
List<String> hwndList = WindowsEnumeratedXml.evaluateXpathGetValues(item, "//@hwnd");
|
||||
if (hwndList.size() > 0)
|
||||
resultHwndStr = hwndList.get(0).replaceAll("[^\\d-.]", ""); //get first hwnd;
|
||||
}
|
||||
else
|
||||
resultStr = item;
|
||||
break;
|
||||
}
|
||||
String newXml = "";
|
||||
Map<String, WindowInfo> infoList;
|
||||
if (resultHwndStr.contains("-")) { //uiabridge target refresh
|
||||
resultHwndStr = resultHwndStr.split("-")[1];
|
||||
infoList = WindowsEnumeratedXml.EnumerateWindowsWithUiaBridge(uiabridge, resultHwndStr, "*");
|
||||
newXml = WindowsEnumeratedXml.generateWindowsXml(infoList, "updates");
|
||||
//System.out.println("newXml: " + newXml);
|
||||
}
|
||||
else
|
||||
{ // native target refresh
|
||||
|
||||
}
|
||||
|
||||
int pos = WIN_XML.indexOf(resultStr);
|
||||
WIN_XML = WIN_XML.substring(0, pos) + newXml + WIN_XML.substring(pos + resultStr.length());
|
||||
|
||||
}
|
||||
|
||||
public String getWindowTypeWithXpath(String xpath) {
|
||||
String result = "";
|
||||
double secondsFromLastUpdate = ((double)(System.nanoTime() - LAST_UPDATED_XML) / 1000000000);
|
||||
|
||||
@@ -64,6 +64,13 @@ public class MainCommands extends BaseCommand {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean cmdTargetRefresh(String[] args) {
|
||||
if (!checkArgumentLength(args, 1))
|
||||
return false;
|
||||
targetXmlRefresh(args[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean cmdWaitForTitle(String[] args) {
|
||||
if (!checkArgumentLength(args, 1))
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user