转自:http://dengpeng.sensorapp.net/?p=495
标准的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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
|
#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 successful\n"
);
}
if
(hookHandle_kb == NULL)
{
printf
(
"C++: Java_SysHook_registerKeyHook - Hook failed!\n"
);
return
;
}
else
{
printf
(
"C++: Java_SysHook_registerKeyHook - Hook successful\n"
);
}
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 failed\n"
);
}
else
{
printf
(
"C++: Java_SysHook_registerKeyHook - Unhook successful\n"
);
}
if
(!UnhookWindowsHookEx(hookHandle_ms))
{
printf
(
"C++: Java_SysHook_registerKeyHook - Unhook failed\n"
);
}
else
{
printf
(
"C++: Java_SysHook_registerKeyHook - Unhook successful\n"
);
}
}
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);
}
|
SysHook.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
/* 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
|
KeyboardEventListener.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
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
;
}
}
|
MouseEventListenter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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;
}
}
|
GlobalEventListener.java :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
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);
}
}
}
}
|
SysHook.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
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();
}
|
Example.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
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());
}
}
|
C++文件需要用Visual Studio编译为你的目标系统的DLL文件。如果是标准32位Windows XP,可以在这里下载已编译的文件。
在Eclipse创建工程后,需要做的设置是将该DLL所在目录添加到Native library location如图:
本文摘录、翻译并修改自http://www.jotschi.de/?p=90