首页 > 解决方案 > 在线程中运行消息循环

问题描述

我想使用 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)在这种情况下也不起作用。

标签: javajna

解决方案


我发现该解决方案实际上已经由 JNA 平台在User32Util.MessageLoopThread中提供


推荐阅读