2012-05-21 2 views
1

Я использую следующий код для прослушивания глобальных ключевых событий:Ключевой прослушиватель написан на Java JNA. Не удается остановить нить

Win32HookManager.java

import com.sun.jna.platform.win32.Kernel32; 
import com.sun.jna.platform.win32.User32; 
import com.sun.jna.platform.win32.WinDef.HMODULE; 
import com.sun.jna.platform.win32.WinDef.LRESULT; 
import com.sun.jna.platform.win32.WinDef.WPARAM; 
import com.sun.jna.platform.win32.WinUser; 
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; 


import java.awt.event.KeyEvent; 

public class Win32HookManager { 
    private static HHOOK keyboardHook; 

    public static boolean installKeyboardHook(final NativeKeyboardListener listener) { 
     final User32 lib = User32.INSTANCE; 
     final HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle(null); 

     final LowLevelKeyboardProc keyboardHookProc = new LowLevelKeyboardProc() { 
      @Override 
      public LRESULT callback(int nCode, WPARAM wParam, KBDLLHOOKSTRUCT info) { 
       NativeKeyboardEvent ev = null; 
       long    ti = System.currentTimeMillis(); 
       boolean    nh = true; 

       if (nCode >= 0) { 
        switch (wParam.intValue()) { 
         case WinUser.WM_KEYDOWN: 
         case WinUser.WM_SYSKEYDOWN: 
          ev = new NativeKeyboardEvent(KeyEvent.KEY_PRESSED, ti, 0, info.vkCode); 
          nh = listener.keyPressed(ev); 
          break; 

         case WinUser.WM_KEYUP: 
         case WinUser.WM_SYSKEYUP: 
          ev = new NativeKeyboardEvent(KeyEvent.KEY_RELEASED, ti, 0, info.vkCode); 
          nh = listener.keyReleased(ev); 
          break; 
        } 
       } 

       if(nh) { 
        return lib.CallNextHookEx(keyboardHook, nCode, wParam, info.getPointer()); 
       } 
       return new LRESULT(1); 
      } 
     }; 

     new Thread() { 
      @Override 
      public void run() { 
       keyboardHook = lib.SetWindowsHookEx(WinUser.WH_KEYBOARD_LL, keyboardHookProc, hMod, 0); 
       msgLoop(); 
       lib.UnhookWindowsHookEx(keyboardHook); 
      } 
     }.start(); 

     return keyboardHook != null; 
    } 

    public static boolean uninstallKeyboardHook() { 
     if(keyboardHook != null) { 
      return User32.INSTANCE.UnhookWindowsHookEx(keyboardHook); 
     } 

     return false; 
    } 

    private static void msgLoop() 
    { 
     final User32 lib = User32.INSTANCE; 

     int result; 
     MSG msg = new MSG(); 
     while ((result = lib.GetMessage(msg, null, 0, 0)) != 0) { 
      if (result == -1) { 
       System.err.println("error in get message"); 
       break; 
      } 
      else { 
       System.err.println("got message"); 
       lib.TranslateMessage(msg); 
       lib.DispatchMessage(msg); 
      } 
     } 
    } 
} 

NativeKeyboardListener

public interface NativeKeyboardListener { 
    public boolean keyPressed (NativeKeyboardEvent e); 
    public boolean keyReleased(NativeKeyboardEvent e); 
} 

NativeKeyboardEvent

public class NativeKeyboardEvent { 
    private int id; 
    private int keyCode; 

    public NativeKeyboardEvent(int id, long when, int modifiers, int keyCode) { 
     this.id  = id; 
     this.keyCode = keyCode; 
    } 

    public int getId() { 
     return id; 
    } 

    public int getKeyCode() { 
     return keyCode; 
    } 
} 

К сожалению, он не работает, как я то есть он обнаруживает, когда клавиша нажата/отпущена, но она не может завершить поток, запущенный методом installKeyboardHook() из-за GetMessage() в методе msgLoop. Да, я могу перестать слушать ключевые события, но я не могу остановить поток. Однако, похоже, что в этом коде требуется GetMessage(). Вы видите какие-либо способы решения этой проблемы?

Спасибо!

ответ

3

Как подсказывает @SLaks, вам нужен какой-то флаг, чтобы указать, должен ли цикл сообщения продолжать работать. Затем вы можете использовать PeekMessage (JNA Doc), который, как и GetMessage, извлекает сообщение из очереди, но не является блокировкой. Затем ваш цикл сообщений должен быть изменен примерно на следующее:

while (!shouldQuit) { 
    while ((result = lib.PeekMessage(msg, null, 0, 0, 1)) != 0) { 
     // ... 
    } 
} 
1

Вы должны сделать флаг private boolean shouldQuit, и, если флаг имеет значение true, выйдите из цикла сообщений.

Затем, чтобы остановить поток, просто установите этот флаг в true.

+0

Это не сработает. Без GetMessage метод не уведомляет о ключевых событиях. И это блокировка потоков. – peter

+1

@ user389658: Вы неправильно поняли мой ответ. Используйте поле флага для завершения цикла, когда хотите. 'if (shouldQuit) break;' – SLaks

+0

@ user389658 Вы можете установить логическое значение 'true', а затем прервать поток ... – Romain

0

Не устанавливайте и не удаляйте поток сообщений.

Только GetMessage обработка (и проверка выхода) должна произойти в дополнительном Thread (который должен быть потоком демона, BTW). Установка и удаление крючка должна происходить в другом месте.

Посмотрите на вклад keyboard hook в JNA для примеров надлежащей процедуры.

+0

Ссылка мертва. Новое местоположение: https://github.com/thepaul/libjna-java/blob/master/contrib/w32keyhook/KeyHook.java – RealHowTo

Смежные вопросы