标准的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






