javajna

How to use JNA with java to detect clicks


If I try to write the code below, I get the following error

Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'SetWindowsHookEx': the specified procedure is not found.

at com.sun.jna.Function.<init>(Function.java:252)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:620)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:596)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:582)
at com.sun.jna.Library$Handler.invoke(Library.java:248)
at jdk.proxy1/jdk.proxy1.$Proxy0.SetWindowsHookEx(Unknown Source)
at tester.test.main(test.java:38)

I'm using machine translation, so this English text could be wrong. Also, since this is my first time asking this question, it would be great if you could tell me where I could improve.

I wrote this code thinking that the message would appear in the console when clicked. But I got an error that I don't understand.

package tester;

import com.sun.jna.Native;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinUser;

public class test {
    public interface User32Ext extends User32 {
        User32Ext INSTANCE = Native.load("user32", User32Ext.class);

        int WM_LBUTTONDOWN = 0x0201;
        int WM_RBUTTONDOWN = 0x0204;

        WinUser.HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, WinDef.HINSTANCE hMod, WinDef.DWORD dwThreadId);

        WinDef.LRESULT CallNextHookEx(WinUser.HHOOK hhk, int nCode, WinDef.WPARAM wParam, WinUser.MSLLHOOKSTRUCT lParam);

        boolean UnhookWindowsHookEx(WinUser.HHOOK hhk);
    }

    public static interface HOOKPROC extends WinUser.LowLevelMouseProc {
    }

    public static void main(String args[]) {
        User32Ext user32 = User32Ext.INSTANCE;
        HOOKPROC hookProc = (int nCode, WinDef.WPARAM wParam, WinUser.MSLLHOOKSTRUCT lParam) -> {
            if (nCode >= 0) {
                if (wParam.intValue() == User32Ext.WM_LBUTTONDOWN) {
                    System.out.println("L");
                } else if (wParam.intValue() == User32Ext.WM_RBUTTONDOWN) {
                    System.out.println("R");
                }
            }
            return user32.CallNextHookEx(null, nCode, wParam, lParam);
        };

        WinUser.HHOOK hhk = user32.SetWindowsHookEx(WinUser.WH_MOUSE_LL, hookProc, null, 0);

        if (hhk == null) {
            System.err.println("Failed to set up a hook.");
            System.exit(1);
        }
        WinUser.MSG msg = new WinUser.MSG();
        while (User32.INSTANCE.GetMessage(msg, null, 0, 0) != 0) {
            User32.INSTANCE.TranslateMessage(msg);
            User32.INSTANCE.DispatchMessage(msg);
        }

        user32.UnhookWindowsHookEx(hhk);
    }
}


Solution

  • It is not much different from registering and creating any other handler.

    First you need to create the handler in an appropriate class implementing a NativeMouseListener (the libraries are in org.jnativehook.*):

    public void nativeMouseClicked(NativeMouseEvent e) {
        if (e.getButton() == NativeMouseEvent.BUTTON2) {
            // do something
        }
    }
    

    (you can add nativeMousePressed and nativeMouseReleased as well like this at this point)

    Secondly you need to register the handler:

        try {
            GlobalScreen.registerNativeHook();
            GlobalScreen.addNativeMouseListener(new YourClass());
        } catch (Exception e) {
            e.printStackTrace();
        }
    

    I have only showed the part of the code which helps you going forward from this point. If you have questions - just ask please.