Added Global Keyboard Hook for refreshing using Ctrl+Shift+3

Created a Jna Global Keyboard Hook class and added hot key for
refreshing the windows xml (Ctrl + Shift + 3).
Fixed cancel button to dispose window properly.
This commit is contained in:
Edward Jakubowski
2014-04-21 19:45:34 -04:00
parent b5082e2b22
commit 7a267a6d9a
8 changed files with 247 additions and 10 deletions

View File

@@ -115,6 +115,7 @@ public class Api {
public static int VK_RMENU = 0xA5; public static int VK_RMENU = 0xA5;
public static int WM_COMMAND = 0x111; public static int WM_COMMAND = 0x111;
public static int MN_GETHMENU = 0x01E1;
public static int CWP_ALL = 0x0000; // Does not skip any child windows public static int CWP_ALL = 0x0000; // Does not skip any child windows

View File

@@ -0,0 +1,199 @@
/*
* Copyright 2014, Synthuse.org
* Released under the Apache Version 2.0 License.
*
* last modified by ejakubowski7@gmail.com
*/
package org.synthuse;
//import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.sun.jna.*;
import com.sun.jna.platform.win32.WinUser.*;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.win32.W32APIOptions;
public class KeyboardHook implements Runnable{
// Keyboard event class, interface, and array list
public static class TargetKeyPress {
int targetKeyCode;
boolean withShift, withCtrl, withAlt;
public TargetKeyPress (int targetKeyCode) {
this.targetKeyCode = targetKeyCode;
this.withShift = false;
this.withCtrl = false;
this.withAlt = false;
}
public TargetKeyPress (int targetKeyCode, boolean withShift, boolean withCtrl, boolean withAlt) {
this.targetKeyCode = targetKeyCode;
this.withShift = withShift;
this.withCtrl = withCtrl;
this.withAlt = withAlt;
}
}
public static List<TargetKeyPress> targetList = Collections.synchronizedList(new ArrayList<TargetKeyPress>());// all keys we want to throw events on
public static interface KeyboardEvents {
void keyPressed(TargetKeyPress target);
}
public KeyboardEvents events = new KeyboardEvents() {
public void keyPressed(TargetKeyPress target) {
//System.out.println("target key pressed: " + target.targetKeyCode);
}
};
// JNA constants and functions
public static final int WH_KEYBOARD_LL = 13;
//Modifier key vkCode constants
public static final int VK_SHIFT = 0x10;
public static final int VK_CONTROL = 0x11;
public static final int VK_MENU = 0x12;
public static final int VK_CAPITAL = 0x14;
public static HHOOK hHook = null;
public static LowLevelKeyboardProc lpfn;
public static volatile boolean quit = false;
public interface User32 extends W32APIOptions {
User32 instance = (User32) Native.loadLibrary("user32", User32.class, DEFAULT_OPTIONS);
LRESULT LowLevelKeyboardProc(int nCode,WPARAM wParam,LPARAM lParam);
HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HMODULE hMod, int dwThreadId);
LRESULT CallNextHookEx(HHOOK idHook, int nCode, WPARAM wParam, LPARAM lParam);
LRESULT CallNextHookEx(HHOOK idHook, int nCode, WPARAM wParam, Pointer lParam);
boolean PeekMessage(MSG lpMsg, HWND hWnd, int wMsgFilterMin, int wMsgFilterMax, int wRemoveMsg);
boolean UnhookWindowsHookEx(HHOOK idHook);
short GetKeyState(int nVirtKey);
//public static interface HOOKPROC extends StdCallCallback {
// LRESULT callback(int nCode, WPARAM wParam, KBDLLHOOKSTRUCT lParam);
//}
}
public interface Kernel32 extends W32APIOptions {
Kernel32 instance = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class, DEFAULT_OPTIONS);
HMODULE GetModuleHandle(String name);
}
// Create Global Windows Keyboard hook and wait until quit == true
public void createGlobalKeyboardHook() {
if (hHook != null)
return; //hook already running don't add anymore
HMODULE hMod = Kernel32.instance.GetModuleHandle(null);
HOOKPROC lpfn = new HOOKPROC() {
@SuppressWarnings("unused")
public LRESULT callback(int nCode, WPARAM wParam, KBDLLHOOKSTRUCT lParam) {
//System.out.println("here " + lParam.vkCode);
TargetKeyPress target = getTargetKeyPressed(lParam.vkCode); //find if this is a target key pressed
if (target != null)
events.keyPressed(target);
//if (lParam.vkCode == 87) //w
// quit = true;
return User32.instance.CallNextHookEx(hHook, nCode, wParam, lParam.getPointer());
}
};
hHook = User32.instance.SetWindowsHookEx(WH_KEYBOARD_LL, lpfn, hMod, 0);
if (hHook == null)
return;
MSG msg = new MSG();
try {
while (!quit) {
User32.instance.PeekMessage(msg, null, 0, 0, 0);
Thread.sleep(10);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//unhook the Global Windows Keyboard hook
public void unhook() {
if (hHook == null)
return;
if (!User32.instance.UnhookWindowsHookEx(hHook))
System.out.println("Failed to unhook");
//System.out.println("Unhooked");
hHook = null;
}
//stops Keyboard hook and causes the unhook command to be called
public static void stopGlobalKeyboardHook() {
quit = true;
}
// search target keyboard event list for a match and return it otherwise return null if no match
public TargetKeyPress getTargetKeyPressed(int keyCode) {
TargetKeyPress target = null;
for (TargetKeyPress tkp : KeyboardHook.targetList) {
if (tkp.targetKeyCode != keyCode)
continue;
if (!tkp.withShift || ((User32.instance.GetKeyState(VK_SHIFT) & 0x8000) != 0)) {
if (!tkp.withCtrl || ((User32.instance.GetKeyState(VK_CONTROL) & 0x8000) != 0)) {
if (!tkp.withAlt || ((User32.instance.GetKeyState(VK_MENU) & 0x8000) != 0)) {
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));
}
// add more target keys to watch for
public static void addKeyEvent(int targetKeyCode) {
KeyboardHook.targetList.add(new TargetKeyPress(targetKeyCode));
}
@Override
public void run() {
createGlobalKeyboardHook();
unhook();//wait for quit == true then unhook
}
public KeyboardHook() {
}
public KeyboardHook(KeyboardEvents events) {
this.events = events;
}
public static void StartGlobalKeyboardHookThreaded(KeyboardEvents events) {
Thread t = new Thread(new KeyboardHook(events));
t.start();
}
/*
// testing
public static void main(String[] args) throws Exception {
//add target keys
KeyboardHook.addKeyEvent(KeyEvent.VK_3, true, true, false);
KeyboardHook.addKeyEvent(KeyEvent.VK_5, false, true, false);
KeyboardHook.addKeyEvent(KeyEvent.VK_Q);
//add global hook and event
KeyboardHook.StartGlobalKeyboardHookThreaded(new KeyboardHook.KeyboardEvents() {
@Override
public void keyPressed(KeyboardHook.TargetKeyPress target) {
System.out.println("target key pressed " + target.targetKeyCode);
if (target.targetKeyCode == KeyEvent.VK_Q){ // if Q was pressed then unhook
KeyboardHook.stopGlobalKeyboardHook();
System.out.println("unhooking");
}
}
});
}
*/
}

View File

@@ -1,3 +1,10 @@
/*
* Copyright 2014, Synthuse.org
* Released under the Apache Version 2.0 License.
*
* last modified by ejakubowski7@gmail.com
*/
package org.synthuse; package org.synthuse;
import java.math.BigInteger; import java.math.BigInteger;

View File

@@ -321,7 +321,7 @@ public class SynthuseDlg extends JFrame {
btnCancel.addActionListener(new ActionListener() { btnCancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) { public void actionPerformed(ActionEvent arg0) {
dialogResult = ""; dialogResult = "";
SynthuseDlg.this.dispose(); SynthuseDlg.this.disposeWindow();
} }
}); });
btnCancel.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_CANCEL_IMG))); btnCancel.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_CANCEL_IMG)));
@@ -383,12 +383,24 @@ public class SynthuseDlg extends JFrame {
this.addWindowListener(new WindowAdapter() { this.addWindowListener(new WindowAdapter() {
@Override @Override
public void windowClosing(WindowEvent arg0) { public void windowClosing(WindowEvent arg0) {
KeyboardHook.stopGlobalKeyboardHook(); //stop keyboard hook
config.save(); config.save();
SynthuseDlg.this.dispose(); // force app to close SynthuseDlg.this.dispose(); // force app to close
} }
}); });
KeyboardHook.addKeyEvent(KeyEvent.VK_3, true, true, false);// refresh xml when CTRL+SHIFT+3 is pressed
//add global hook and event
KeyboardHook.StartGlobalKeyboardHookThreaded(new KeyboardHook.KeyboardEvents() {
@Override
public void keyPressed(KeyboardHook.TargetKeyPress target) {
//System.out.println("target key pressed " + target.targetKeyCode);
if (target.targetKeyCode == KeyEvent.VK_3){
btnRefresh.doClick();
}
}
});
btnRefresh.doClick(); btnRefresh.doClick();
refreshDatabinding(); refreshDatabinding();
} }
@@ -450,4 +462,10 @@ public class SynthuseDlg extends JFrame {
XpathManager.buildXpathStatementThreaded(hwnd, runtimeId, textPane, xpathEvents); XpathManager.buildXpathStatementThreaded(hwnd, runtimeId, textPane, xpathEvents);
} }
} }
public void disposeWindow()
{
WindowEvent closingEvent = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(closingEvent);
}
} }

View File

@@ -1,3 +1,10 @@
/*
* Copyright 2014, Synthuse.org
* Released under the Apache Version 2.0 License.
*
* last modified by ejakubowski7@gmail.com
*/
package org.synthuse; package org.synthuse;
import com.sun.jna.platform.win32.WinDef.HWND; import com.sun.jna.platform.win32.WinDef.HWND;

View File

@@ -91,6 +91,7 @@ public class WindowInfo {
extra = new LinkedHashMap<String, String>(); extra = new LinkedHashMap<String, String>();
extra.put("tvCount", tvCount.intValue() + ""); extra.put("tvCount", tvCount.intValue() + "");
} }
//check if window has a menu //check if window has a menu
HMENU hmenu = Api.User32.instance.GetMenu(hWnd); HMENU hmenu = Api.User32.instance.GetMenu(hWnd);
if (hmenu != null) { //menu item count if (hmenu != null) { //menu item count
@@ -99,6 +100,12 @@ public class WindowInfo {
this.menus = menuCount; this.menus = menuCount;
this.menu = hmenu; this.menu = hmenu;
} }
else
{
LRESULT result = Api.User32.instance.PostMessage(hWnd, Api.MN_GETHMENU, new WPARAM(0), new LPARAM());
if (result.longValue() != 1)
System.out.println("MN_GETHMENU: " + result.longValue());
}
} }
if (isChild) { if (isChild) {

View File

@@ -16,6 +16,7 @@ import java.util.Date;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JTextPane; import javax.swing.JTextPane;
@@ -45,7 +46,7 @@ import com.sun.jna.platform.win32.WinDef.HWND;
public class WindowsEnumeratedXml implements Runnable{ public class WindowsEnumeratedXml implements Runnable{
public static Exception lastException = null; public static Exception lastException = null;
public static AtomicBoolean enumeratingXmlFlag = new AtomicBoolean(false);
public JTextPane outputPane = null; public JTextPane outputPane = null;
public JLabel lblStatus = null; public JLabel lblStatus = null;
public WindowsEnumeratedXml() { public WindowsEnumeratedXml() {
@@ -64,9 +65,13 @@ public class WindowsEnumeratedXml implements Runnable{
outputPane.setCaretPosition(0); outputPane.setCaretPosition(0);
double seconds = ((double)(System.nanoTime() - startTime) / 1000000000); double seconds = ((double)(System.nanoTime() - startTime) / 1000000000);
lblStatus.setText("Windows Enumerated Xml loaded in " + new DecimalFormat("#.###").format(seconds) + " seconds"); lblStatus.setText("Windows Enumerated Xml loaded in " + new DecimalFormat("#.###").format(seconds) + " seconds");
enumeratingXmlFlag.set(false);
} }
public static void getXmlThreaded(JTextPane outputPane, JLabel lblStatus) { 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)); Thread t = new Thread(new WindowsEnumeratedXml(outputPane, lblStatus));
t.start(); t.start();
} }

View File

@@ -88,13 +88,6 @@ public class WinApiTest {
String menuTxt = api.GetMenuItemText(hmenu, m); String menuTxt = api.GetMenuItemText(hmenu, m);
System.out.println("Menu Text: " + menuTxt); 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());
}*/
} }
} }