标准的Java键盘事件监听器(KeyListener)和鼠标事件监听器(MouseListener)只能在该Java程序聚焦的时候监听事件。要想让你的Java程序能够在系统后台跟踪全局键盘和鼠标事件,那就需要使用JNI(Java Native Interface)来创建一个钩子监听操作系统的事件了。本文只讨论,Java程序与Windows操作系统的交互,如果你知道如何实现Java监听Linux事件,请留言,谢谢。开发运行环境:Windows XP SP3, Java 1.6_15, Eclipse 3.5
直接上代码
SysHook.cpp:
[cpp]
#include <windows.h>
#include "SysHook.h"
#include <jni.h>
HINSTANCE hInst = NULL;
JavaVM * jvm = NULL;
jobject hookObj_kb = NULL;
jobject hookObj_ms = NULL;
jobject g_kl = NULL;
jmethodID processKeyID_kb = NULL;
jmethodID processKeyID_ms = NULL;
DWORD hookThreadId = 0;
LONG g_mouseLocX = -1; // x-location of mouse position
LONG g_mouseLocY = -1; // y-location of mouse position
extern "C"
BOOL APIENTRY DllMain(HINSTANCE _hInst, DWORD reason, LPVOID reserved)
{
switch (reason)
{
case DLL_PROCESS_ATTACH:
printf("C++: DllMain – DLL_PROCESS_ATTACH.n");
hInst = _hInst;
break;
default:
break;
}
return TRUE;
}
LRESULT CALLBACK MouseTracker(int nCode, WPARAM wParam, LPARAM lParam)
{
JNIEnv * env;
KBDLLHOOKSTRUCT * p = (KBDLLHOOKSTRUCT *)lParam;
if (jvm->AttachCurrentThread((void **)&env, NULL) >= 0)
{
if (nCode==HC_ACTION)
{
MOUSEHOOKSTRUCT* pStruct = (MOUSEHOOKSTRUCT*)lParam;
if (pStruct->pt.x != g_mouseLocX || pStruct->pt.y != g_mouseLocY)
{
env->CallVoidMethod(hookObj_ms, processKeyID_ms, (jint)pStruct->pt.x,(jint)pStruct->pt.y, g_kl);
g_mouseLocX = pStruct->pt.x;
g_mouseLocY = pStruct->pt.y;
}
}
}
else
{
printf("C++: LowLevelKeyboardProc – Error on the attach current thread.n");
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
JNIEnv * env;
KBDLLHOOKSTRUCT * p = (KBDLLHOOKSTRUCT *)lParam;
if (jvm->AttachCurrentThread((void **)&env, NULL) >= 0)
{
switch (wParam)
{
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
env->CallVoidMethod(hookObj_kb, processKeyID_kb, (jboolean)TRUE, p->vkCode,g_kl);
break;
case WM_KEYUP:
case WM_SYSKEYUP:
env->CallVoidMethod(hookObj_kb, processKeyID_kb, (jboolean)FALSE, p->vkCode,g_kl);
break;
default:
break;
}
}
else
{
printf("C++: LowLevelKeyboardProc – Error on the attach current thread.n");
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
void MsgLoop()
{
MSG message;
while (GetMessage(&message, NULL, 0, 0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
}
JNIEXPORT void JNICALL Java_SysHook_registerHook(JNIEnv * env, jobject obj,jobject kl)
{
HHOOK hookHandle_ms = SetWindowsHookEx(WH_MOUSE_LL, MouseTracker, hInst, 0);
HHOOK hookHandle_kb = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hInst, 0);
g_kl = kl;
if (hookHandle_ms == NULL)
{
printf("C++: Java_SysHook_registerKeyHook – Hook failed!n");
return;
}
else
{
printf("C++: Java_SysHook_registerKeyHook – Hook successfuln");
}
if (hookHandle_kb == NULL)
{
printf("C++: Java_SysHook_registerKeyHook – Hook failed!n");
return;
}
else
{
printf("C++: Java_SysHook_registerKeyHook – Hook successfuln");
}
hookObj_kb = env->NewGlobalRef(obj);
jclass cls_kb = env->GetObjectClass(hookObj_kb);
processKeyID_kb = env->GetMethodID(cls_kb, "processKey", "(ZILGlobalEventListener;)V");
hookObj_ms = env->NewGlobalRef(obj);
jclass cls_ms = env->GetObjectClass(hookObj_ms);
processKeyID_ms = env->GetMethodID(cls_ms, "mouseMoved", "(IILGlobalEventListener;)V");
env->GetJavaVM(&jvm);
hookThreadId = GetCurrentThreadId();
MsgLoop();
if (!UnhookWindowsHookEx(hookHandle_kb))
{
printf("C++: Java_SysHook_registerKeyHook – Unhook failedn");
}
else
{
printf("C++: Java_SysHook_registerKeyHook – Unhook successfuln");
}
if (!UnhookWindowsHookEx(hookHandle_ms))
{
printf("C++: Java_SysHook_registerKeyHook – Unhook failedn");
}
else
{
printf("C++: Java_SysHook_registerKeyHook – Unhook successfuln");
}
}
JNIEXPORT void JNICALL Java_SysHook_unRegisterHook(JNIEnv *env, jobject object)
{
if (hookThreadId == 0)
return;
printf("C++: Java_SysHook_unRegisterKeyHook – call PostThreadMessage.n");
PostThreadMessage(hookThreadId, WM_QUIT, 0, 0L);
}
[/cpp]
SysHook.h:
[cpp]
/* DO NOT EDIT THIS FILE – it is machine generated */
#include <jni.h>
/* Header for class SysHook */
#ifndef _Included_SysHook
#define _Included_SysHook
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: SysHook
* Method: registerHook
* Signature: (LGlobalEventListener;)V
*/
JNIEXPORT void JNICALL Java_SysHook_registerHook (JNIEnv *, jobject, jobject);
/*
* Class: SysHook
* Method: unRegisterHook
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_SysHook_unRegisterHook (JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
[/cpp]
KeyboardEventListener.java
[java]
import java.util.*;
public interface KeyboardEventListener extends EventListener {
public void GlobalKeyPressed(KeyboardEvent event);
public void GlobalKeyReleased(KeyboardEvent event);
}
class KeyboardEvent extends EventObject {
private static final long serialVersionUID = 2341653211621224652L;
boolean ts, ap, ek;
int vk;
public KeyboardEvent(Object source, boolean ts, int vk, boolean ap,
boolean ek) {
super(source);
this.ts = ts;
this.vk = vk;
this.ap = ap;
this.ek = ek;
}
public boolean getTransitionState() {
return ts;
}
public long getVirtualKeyCode() {
return vk;
}
public boolean isAltPressed() {
return ap;
}
public boolean isExtendedKey() {
return ek;
}
public boolean equals(KeyboardEvent event) {
if (event.getVirtualKeyCode() == vk) {
if (event.isExtendedKey() == ek) {
if (event.isAltPressed() == ap) {
return true;
}
}
}
return false;
}
}
[/java]
MouseEventListenter.java
[java]
import java.util.*;
public interface MouseEventListener extends EventListener {
public void GlobalMouseX(MouseEvent event);
public void GlobalMouseY(MouseEvent event);
}
class MouseEvent extends EventObject {
private static final long serialVersionUID = 14654L;
int cord_x, cord_y;
public MouseEvent(Object source, int cord_x, int cord_y) {
super(source);
this.cord_x = cord_x;
this.cord_y = cord_y;
}
public int getMouseX() {
return cord_x;
}
public int getMouseY() {
return cord_y;
}
}
[/java]
GlobalEventListener.java :
[java]
public class GlobalEventListener {
PoolHook pt;
public GlobalEventListener() {
pt = new PoolHook(this);
pt.start();
}
protected javax.swing.event.EventListenerList listenerList = new javax.swing.event.EventListenerList();
public void addKeyboardEventListener(KeyboardEventListener listener) {
listenerList.add(KeyboardEventListener.class, listener);
}
public void removeKeyboardEventListener(KeyboardEventListener listener) {
listenerList.remove(KeyboardEventListener.class, listener);
}
public void addMouseEventListener(MouseEventListener listener) {
listenerList.add(MouseEventListener.class, listener);
}
public void removeMouseEventListener(MouseEventListener listener) {
listenerList.remove(MouseEventListener.class, listener);
}
void keyPressed(KeyboardEvent event) {
Object[] listeners = listenerList.getListenerList();
for (int i = 0; i < listeners.length; i += 2) {
if (listeners[i] == KeyboardEventListener.class) {
((KeyboardEventListener) listeners[i + 1])
.GlobalKeyPressed(event);
}
}
}
void mouseMoved(MouseEvent event) {
Object[] listeners = listenerList.getListenerList();
for (int i = 0; i < listeners.length; i += 2) {
if (listeners[i] == MouseEventListener.class) {
((MouseEventListener) listeners[i + 1]).GlobalMouseX(event);
((MouseEventListener) listeners[i + 1]).GlobalMouseY(event);
}
}
}
void keyReleased(KeyboardEvent event) {
Object[] listeners = listenerList.getListenerList();
for (int i = 0; i < listeners.length; i += 2) {
if (listeners[i] == KeyboardEventListener.class) {
((KeyboardEventListener) listeners[i + 1])
.GlobalKeyReleased(event);
}
}
}
}
[/java]
SysHook.java:
[java]
class PoolHook extends Thread {
SysHook hook;
GlobalEventListener g_gl;
PoolHook(GlobalEventListener gl) {
g_gl = gl;
}
public void run() {
hook = new SysHook();
hook.registerHook(g_gl);
}
}
class SysHook {
static {
System.loadLibrary("SysHook");
}
void processKey(boolean ts, int vk, GlobalEventListener gl) {
KeyboardEvent event = new KeyboardEvent(this, ts, vk, false, false);
gl.keyPressed(event);
}
void mouseMoved(int cord_x, int cord_y, GlobalEventListener gl) {
MouseEvent event = new MouseEvent(this, cord_x, cord_y);
gl.mouseMoved(event);
}
native void registerHook(GlobalEventListener gl);
native void unRegisterHook();
}
[/java]
Example.java:
[java]
public class Example implements KeyboardEventListener, MouseEventListener {
static GlobalEventListener gl;
public static void main(String[] args) throws Exception {
Example inst = new Example();
gl = new GlobalEventListener();
gl.addKeyboardEventListener(inst);
gl.addMouseEventListener(inst);
}
@Override
public void GlobalKeyPressed(KeyboardEvent event) {
System.out.println("Key Pressed: " + event.getVirtualKeyCode());
}
@Override
public void GlobalKeyReleased(KeyboardEvent event) {
}
@Override
public void GlobalMouseX(MouseEvent event) {
System.out.println("Mouse X: " + event.getMouseX());
}
@Override
public void GlobalMouseY(MouseEvent event) {
System.out.println("Mouse Y: " + event.getMouseY());
}
}
[/java]
C++文件需要用Visual Studio编译为你的目标系统的DLL文件。如果是标准32位Windows XP,可以在这里下载已编译的文件。
在Eclipse创建工程后,需要做的设置是将该DLL所在目录添加到Native library location如图:
本文摘录、翻译并修改自http://www.jotschi.de/?p=90