在线程中运行消息循环

时间:2019-01-15 20:39:55

标签: java jna

我想使用JNA运行低级键盘挂钩。我改编了一个在JNA contrib文件夹中找到的示例

private static HHOOK keyHook;
private static LowLevelKeyboardProc keyCallback;

public static void main(String[] args) {
   final User32 U32 = User32.INSTANCE;
   final Kernel32 K32 = Kernel32.INSTANCE;

   HMODULE module = K32.GetModuleHandle(null); 
   keyCallback = (int code, WPARAM wParam, KBDLLHOOKSTRUCT info) -> {
      if (code >= 0) {
         System.err.println("Key=" + info.vkCode);
         if (info.vkCode == 81) {
           U32.PostQuitMessage(0);
         }
      }
      long peer = Pointer.nativeValue(info.getPointer());
      return U32.CallNextHookEx(keyHook, code, wParam, new LPARAM(peer));
   };
   keyHook = U32.SetWindowsHookEx(WH_KEYBOARD_LL, keyCallback, module, 0);
   System.out.println("Hook installed, type anywhere, 'q' to quit");

   MSG msg = new MSG();
   while (U32.GetMessage(msg, null, 0, 0) > 0) {         
      U32.TranslateMessage(msg);
      U32.DispatchMessage(msg);               
   }
   U32.UnhookWindowsHookEx(keyHook);
}

这按预期工作。但是现在,我想在线程中运行消息循环,以避免阻塞我的应用程序。天真的方法是

Executor thread = Executors.newSingleThreadExecutor();
thread.execute(() -> {
   int result;
   MSG msg = new MSG();
   while ((result = U32.GetMessage(msg, null, 0, 0)) > 0) {         
      U32.TranslateMessage(msg);
      U32.DispatchMessage(msg);               
   }
   U32.UnhookWindowsHookEx(keyHook);
});

不幸的是,这不起作用。回调不再被调用,系统上的键盘输入开始大量滞后。如何正确处理我的消息循环?我想我需要找到线程ID并将其传递给SetWindowsHookEx而不是0,但是如何获取该ID?

编辑:我通过将整个钩子注册和消息循环封装在一个线程中,尝试了一种不同的方法。回调现在可以正常工作,但是PostQuitMessage似乎没有将消息发布到正确的队列,并且无法停止线程。 PostThreadMessage(0, User32.WM_QUIT, null, null)在这种情况下也不起作用。

1 个答案:

答案 0 :(得分:1)

我发现JNA平台实际上已经在User32Util.MessageLoopThread

中提供了该解决方案。