JDK23中使用java.lang.foreign的原生Hook

问题描述 投票:0回答:1

我想尝试使用完全可以用Java编写的NativeHook,而不是JNativeHook,所以我写了一些测试代码。 但是,无论我按哪个键,输出始终相同。 有人可以告诉我如何获得正确的密钥代码吗?

import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class hook_test {
    private static final int WH_KEYBOARD_LL = 13;
    private static final int WM_KEYDOWN = 0x0100;
    private static long hook;
    private static MethodHandle callNextHookEx;

    void main() throws Throwable {
        System.loadLibrary("user32");
        Linker linker = Linker.nativeLinker();
        SymbolLookup user32Lookup = SymbolLookup.loaderLookup();
        MethodHandle setWindowsHookEx = linker.downcallHandle(user32Lookup.find("SetWindowsHookExA").orElseThrow(), FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.JAVA_INT));
        callNextHookEx = linker.downcallHandle(user32Lookup.find("CallNextHookEx").orElseThrow(), FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.JAVA_LONG, ValueLayout.JAVA_INT, ValueLayout.JAVA_LONG, ValueLayout.JAVA_LONG));
        MethodHandle getMessage = linker.downcallHandle(user32Lookup.find("GetMessageA").orElseThrow(), FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.JAVA_INT));
        MethodHandle unhookWindowsHookEx = linker.downcallHandle(user32Lookup.find("UnhookWindowsHookEx").orElseThrow(), FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_LONG));
        MethodHandle hookProcHandle = MethodHandles.lookup().findStatic(hook_test.class, "hookProc", MethodType.methodType(long.class, int.class, long.class, long.class));
        MemorySegment hookProcAddress = linker.upcallStub(hookProcHandle, FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.JAVA_INT, ValueLayout.JAVA_LONG, ValueLayout.JAVA_LONG), Arena.ofAuto());
        hook = (long) setWindowsHookEx.invoke(WH_KEYBOARD_LL, hookProcAddress, MemorySegment.NULL, 0);

        if (hook == 0) {
            System.out.println("Failed to set hook");
            return;
        }

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try {
                if (hook != 0)
                    unhookWindowsHookEx.invoke(hook);

            } catch (Throwable t) {
                System.err.println(t.getMessage());
            }
        }));
        try (Arena arena = Arena.ofAuto()) {
            MemorySegment msg = arena.allocate(28);
            while ((int) getMessage.invoke(msg, MemorySegment.NULL, 0, 0) != 0) {
            }
        }
    }

    public static long hookProc(int code, long wParam, long lParam) {
        if (code >= 0) {
            if (wParam == WM_KEYDOWN)
                System.out.printf("int code:%s\tlong wParam:%s\tlong lParam:%s\n", code, wParam, lParam);

        }
        try {
            return (long) callNextHookEx.invoke(hook, code, wParam, lParam);
        } catch (Throwable t) {
            System.err.println(t.getMessage());
            return 0;
        }
    }
}

我预计即使没有获得正确的按键代码,对于每个按键输入,lParam 也会输出不同的值。然而,当我尝试运行它时,所有的值都是相同的,并且每次启动时它们都会改变。

int code:0  long wParam:256 long lParam:1059701387608
int code:0  long wParam:256 long lParam:1059701387608
int code:0  long wParam:256 long lParam:1059701387608
int code:0  long wParam:256 long lParam:1059701387608
java windows hook native
1个回答
0
投票

正如 greg-449 在评论中建议的那样,这两个参数不应该很长。这是我的 MinGW 包含的内容:

typedef LRESULT (CALLBACK *HOOKPROC)(int code,WPARAM wParam,LPARAM lParam);
typedef UINT_PTR WPARAM;
typedef LONG_PTR LPARAM;

换句话说,一个是指向 int 的指针,另一个是指向 long 的指针。在您的代码中,您应该使用

MemoryLayout
作为类型,并获取它们的 int 和 long 值。

© www.soinside.com 2019 - 2024. All rights reserved.