介绍
这里有一个例子来说明这个问题。考虑我正在跟踪并向用户显示鼠标全局当前位置以及最后一次单击按钮和位置。这是一张图片:
为了归档在 windows 框上捕获的单击事件,这将被发送到其他程序事件消息队列,我使用 winapi 即user32.dll
库创建了一个钩子。这是在 JDK 沙箱之外,所以我使用 JNA 来调用本机库。
这一切都很完美,但它并没有像我期望的那样关闭。
我的问题是 - 如何正确关闭以下示例程序?
示例源
下面的代码并非完全由我编写,而是取自Oracle 论坛中的这个问题并部分修复。
import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Platform;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.platform.win32.WinUser.HHOOK;
import com.sun.jna.platform.win32.WinUser.HOOKPROC;
import com.sun.jna.platform.win32.WinUser.MSG;
import com.sun.jna.platform.win32.WinUser.POINT;
public class MouseExample {
final JFrame jf;
final JLabel jl1, jl2;
final CWMouseHook mh;
final Ticker jt;
public class Ticker extends Thread {
public boolean update = true;
public void done() {
update = false;
}
public void run() {
try {
Point p, l = MouseInfo.getPointerInfo().getLocation();
int i = 0;
while (update == true) {
try {
p = MouseInfo.getPointerInfo().getLocation();
if (!p.equals(l)) {
l = p;
jl1.setText(new GlobalMouseClick(p.x, p.y)
.toString());
}
Thread.sleep(35);
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
}
} catch (Exception e) {
update = false;
}
}
}
public MouseExample() throws AWTException, UnsupportedOperationException {
this.jl1 = new JLabel("{}");
this.jl2 = new JLabel("{}");
this.jf = new JFrame();
this.jt = new Ticker();
this.jt.start();
this.mh = new CWMouseHook() {
@Override
public void globalClickEvent(GlobalMouseClick m) {
jl2.setText(m.toString());
}
};
mh.setMouseHook();
jf.setLayout(new GridLayout(2, 2));
jf.add(new JLabel("Position"));
jf.add(jl1);
jf.add(new JLabel("Last click"));
jf.add(jl2);
jf.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
mh.dispose();
jt.done();
jf.dispose();
}
});
jf.setLocation(new Point(0, 0));
jf.setPreferredSize(new Dimension(200, 90));
jf.pack();
jf.setVisible(true);
}
public static class GlobalMouseClick {
private char c;
private int x, y;
public GlobalMouseClick(char c, int x, int y) {
super();
this.c = c;
this.x = x;
this.y = y;
}
public GlobalMouseClick(int x, int y) {
super();
this.x = x;
this.y = y;
}
public char getC() {
return c;
}
public void setC(char c) {
this.c = c;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
@Override
public String toString() {
return (c != 0 ? c : "") + " [" + x + "," + y + "]";
}
}
public static class CWMouseHook {
public User32 USER32INST;
public CWMouseHook() throws UnsupportedOperationException {
if (!Platform.isWindows()) {
throw new UnsupportedOperationException(
"Not supported on this platform.");
}
USER32INST = User32.INSTANCE;
mouseHook = hookTheMouse();
Native.setProtected(true);
}
private static LowLevelMouseProc mouseHook;
private HHOOK hhk;
private boolean isHooked = false;
public static final int WM_LBUTTONDOWN = 513;
public static final int WM_LBUTTONUP = 514;
public static final int WM_RBUTTONDOWN = 516;
public static final int WM_RBUTTONUP = 517;
public static final int WM_MBUTTONDOWN = 519;
public static final int WM_MBUTTONUP = 520;
public void dispose() {
unsetMouseHook();
mousehook_thread = null;
mouseHook = null;
hhk = null;
USER32INST = null;
}
public void unsetMouseHook() {
isHooked = false;
USER32INST.UnhookWindowsHookEx(hhk);
System.out.println("Mouse hook is unset.");
}
public boolean isIsHooked() {
return isHooked;
}
public void globalClickEvent(GlobalMouseClick m) {
System.out.println(m);
}
private Thread mousehook_thread;
public void setMouseHook() {
mousehook_thread = new Thread(new Runnable() {
@Override
public void run() {
try {
if (!isHooked) {
hhk = USER32INST.SetWindowsHookEx(14, mouseHook,
Kernel32.INSTANCE.GetModuleHandle(null), 0);
isHooked = true;
System.out
.println("Mouse hook is set. Click anywhere.");
// message dispatch loop (message pump)
MSG msg = new MSG();
while ((USER32INST.GetMessage(msg, null, 0, 0)) != 0) {
USER32INST.TranslateMessage(msg);
USER32INST.DispatchMessage(msg);
if (!isHooked)
break;
}
} else
System.out
.println("The Hook is already installed.");
} catch (Exception e) {
System.err.println("Caught exception in MouseHook!");
}
}
});
mousehook_thread.start();
}
private interface LowLevelMouseProc extends HOOKPROC {
LRESULT callback(int nCode, WPARAM wParam, MOUSEHOOKSTRUCT lParam);
}
private LowLevelMouseProc hookTheMouse() {
return new LowLevelMouseProc() {
@Override
public LRESULT callback(int nCode, WPARAM wParam,
MOUSEHOOKSTRUCT info) {
if (nCode >= 0) {
switch (wParam.intValue()) {
case CWMouseHook.WM_LBUTTONDOWN:
globalClickEvent(new GlobalMouseClick('L',
info.pt.x, info.pt.y));
break;
case CWMouseHook.WM_RBUTTONDOWN:
globalClickEvent(new GlobalMouseClick('R',
info.pt.x, info.pt.y));
break;
case CWMouseHook.WM_MBUTTONDOWN:
globalClickEvent(new GlobalMouseClick('M',
info.pt.x, info.pt.y));
break;
default:
break;
}
}
return USER32INST.CallNextHookEx(hhk, nCode, wParam,
info.getPointer());
}
};
}
public class Point extends Structure {
public class ByReference extends Point implements
Structure.ByReference {
};
public NativeLong x;
public NativeLong y;
}
public static class MOUSEHOOKSTRUCT extends Structure {
public static class ByReference extends MOUSEHOOKSTRUCT implements
Structure.ByReference {
};
public POINT pt;
public HWND hwnd;
public int wHitTestCode;
public ULONG_PTR dwExtraInfo;
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
new MouseExample();
} catch (AWTException e) {
e.printStackTrace();
}
}
});
}
}