4

介绍

这里有一个例子来说明这个问题。考虑我正在跟踪并向用户显示鼠标全局当前位置以及最后一次单击按钮和位置。这是一张图片:

替代文字

为了归档在 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();
                }
            }
        });
    }
}
4

1 回答 1

1

在你的线程中你应该调用

User32.PostQuitMessage(0)

通知本机线程(挂钩)您不再需要它。当你这样做时,在你的代码中检查

而 ((USER32INST.GetMessage(msg, null, 0, 0)) != 0)

看到您不再需要钩子并在本机端终止它。我建议您首先尝试使用内部事件来关闭钩子,例如

CWMouseHook.WM_MBUTTONDOWN

只是看看它是否正常工作。

在这篇文章:JNA 鼠标钩子的工作示例中,您可以看到一些可以帮助您的代码。干杯。

于 2011-01-01T11:46:08.097 回答