我目前正在尝试实现一个全局密钥侦听器,让用户在bot工作时与它进行交互。例如escape退出、F1暂停等。这个代码有效,但它适用于输入的每个键,我正在自学JNA,但我不知道这个代码在哪里执行特定的操作,以及如何更改这些操作/输入以区分它们。
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HINSTANCE;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.platform.win32.WinUser.HOOKPROC;
public class MainTestKeyHook {
public static void main(String[] args) throws Exception {
HOOKPROC hookProc = new HOOKPROC_bg();
HINSTANCE hInst = Kernel32.INSTANCE.GetModuleHandle(null);
User32.HHOOK hHook = User32.INSTANCE.SetWindowsHookEx(User32.WH_KEYBOARD_LL, hookProc, hInst, 0);
if (hHook == null)
return;
User32.MSG msg = new User32.MSG();
System.err.println("Please press any key ....");
while (true) {
User32.INSTANCE.GetMessage(msg, null, 0, 0);
}
}
}
class HOOKPROC_bg implements HOOKPROC {
public HOOKPROC_bg() {
}
public LRESULT callback(int nCode, WPARAM wParam, LPARAM lParam) {
System.err.println("callback bbbnhkilhjkibh nCode: " + nCode);
return new LRESULT(0);
}
}
您要查找的是有关Windows API低级键盘挂钩的信息。
您的回调是LowLevelKeyboardProc
回调,其文档可在此处找到。
文档显示,传递给回调的LPARAM
实际上是指向KBDLLHOOKSTRUCT
结构的指针。该结构的成员vkCode
包含被按下的键的虚拟键代码。请参阅虚拟密钥代码的完整列表。
幸运的是,由于您已经在使用jna-platform
包,因此可以使用现有的类型映射。我建议您遵循JNA全球密钥挂钩演示应用程序的做法:
/* ... */
import com.sun.jna.platform.win32.WinUser.HHOOK;
import com.sun.jna.platform.win32.WinUser.KBDLLHOOKSTRUCT;
import com.sun.jna.platform.win32.WinUser.LowLevelKeyboardProc;
import com.sun.jna.platform.win32.WinUser.MSG;
public class MainTestKeyHook {
public static void main(String[] args) throws Exception {
HINSTANCE moduleHandle = Kernel32.INSTANCE.GetModuleHandle(null);
HHOOK hookHandle;
LowLevelKeyboardProc keyboardHook = new LowLevelKeyboardProc() {
@Override
public LRESULT callback(int nCode, WPARAM wParam, KBDLLHOOKSTRUCT info) {
// LowLevelKeyboardProc docs: "If nCode is less than zero, the hook
// procedure must pass the message to the CallNextHookEx function
// without further processing and should return the value returned
// by CallNextHookEx."
if (nCode >= 0) {
switch (wParam.intValue()) {
// alternatively WM_KEYUP and WM_SYSKEYUP
case WinUser.WM_KEYDOWN:
case WinUser.WM_SYSKEYDOWN:
handleKeyDown(info.vkCode);
}
}
Pointer ptr = info.getPointer();
long peer = Pointer.nativeValue(ptr);
return User32.INSTANCE.CallNextHookEx(hookHandle, nCode, wParam, new LPARAM(peer));
}
};
hookHandle = User32.INSTANCE.SetWindowsHookEx(User32.WH_KEYBOARD_LL, keyboardHook, moduleHandle, 0);
if (hookHandle == null)
return;
System.out.println("Please press any key ...");
int result;
MSG msg = new MSG();
while ((result = User32.INSTANCE.GetMessage(msg, null, 0, 0)) != 0) {
if (result == -1) {
System.err.println("error in GetMessage");
break;
}
User32.INSTANCE.TranslateMessage(msg);
User32.INSTANCE.DispatchMessage(msg);
}
User32.INSTANCE.UnhookWindowsHookEx(hookHandle);
}
// https://learn.microsoft.com/en-us/windows/desktop/inputdev/virtual-key-codes
private static final int VK_F1 = 0x70;
public static void handleKeyDown(int vkCode) {
System.out.println("Key = " + vkCode");
if (vkCode == VK_F1) {
System.out.println("F1 pressed!");
}
}
}
您应该注意到,此示例调用并返回CallNextHookEx
的值。来自文件:
如果nCode小于零,则钩子过程必须返回CallNextHookEx返回的值。
如果nCode大于或等于零,并且钩子过程没有处理该消息,强烈建议您调用CallNextHookEx并返回它返回的值;否则,安装了WH_KEYBOARD_LL钩子的其他应用程序将不会收到钩子通知,因此可能会出现错误行为。如果钩子过程处理了消息,它可能会返回一个非零值,以防止系统将消息传递给钩子链的其余部分或目标窗口过程。
正如最后一句所说,如果密钥与要拦截的密钥匹配,则可以返回一个非零值,以防止该密钥事件到达目标窗口。不过要小心,因为你可能会意外地阻止自己的键盘输入:-(
我还建议您阅读Windows API文档中的Hooks概述。