Kristian Kraljić You may compare programming with poetry,
a harmonic script is a piece of creativity which will last forever.
- Kristian Kraljić

Java – Global (low level) Keyboard / Mouse Hook

by Kristian Kraljic, February 9, 2016

About five years ago I started this blog by picking up an idea of Johannes Schüth (Jotschi) about a global keyboard and mouse listener for Java. Today I am very happy to announce the next major version of the library now available on GitHub!

system-hook-logo

Keyboard and mouse events in Java only work, if the registered component is in focus. For example, in case a window looses its focus (e.g. when minimized), it stops receiving any more events. Through a low-level system-wide hook it’s possible to deliver those events regardless. You’ll find all sources of the latest release, as well as binary bundles / prepackaged Java archives (JAR) on the GitHub project page.

The old post describes all changes I did to the initial version by Johannes (Jotschi). For the new major release I again reworked nearly every part of the library, to make it more stable and versatile. Here is what I did:

  • Optimized the native library C code, fixed bugs and removed all parts requireing a C++ compiler in the first place.
  • Renamed the event classes and listener interfaces and prepended Global to avoid conflicts with existing (Swing) event listeners.
  • Added support for mouseWheel events to the GlobalMouseListener.
  • Again allowed negative "out of bounds" values for GlobalMouseEvents, to also track the mouse pointer off screen (e.g. on multi-monitor setups).
  • Improved the threading concept and implemented a native error handling. The GlobalKeyboardHook and GlobalMouseHook constructors will now throw a UnsatisfiedLinkError if the native libraries can not be loaded or a RuntimeException in case hooking fails.
  • All the code has been moved to GitHub and binaries are now beeing continuously built by AppVeyor. Feel free to contribute on GitHub!

(Again) Last but not least, please share your ideas and problems in the comments. I will try to enhance the Global Keyboard / Mouse Hook based on your feedback. For the next release let’s see if we can get Linux and / or Mac OSX support going.

202 Comments

  1. Pradeep says:

    Fantastic!!! Works soooooooo well!!!
    I have one question though. When we are typing on Windows pop-up windows (username and Password Windows) it doesn’t capture any. Is it because of the security that they are using or do we need to tweak something on this?

    • Hello Pradeep,

      > Is it because of the security that they are using…
      Actually that’s the case, yes. And I don’t think you can do anything about it either, as the low-level hook uses the Windows API, so they have all the mechanisms in hand to prevent your program from capturing the log-in username & password.

      Regards, Kristian

  2. vijay Gharal says:

    I am getting this issue can anyone help one the same ?

    Exception in thread “Thread-0” java.lang.UnsatisfiedLinkError: KeyboardHook.de.ksquared.system.keyboard.KeyboardHook.registerHook(LKeyboardHook/de/ksquared/system/keyboard/GlobalKeyListener;)V
    at KeyboardHook.de.ksquared.system.keyboard.KeyboardHook.registerHook(Native Method)
    at KeyboardHook.de.ksquared.system.keyboard.PoolHook.run(KeyboardHook.java:46)

  3. Raja says:

    Hi Guys,

    this is simply fantastic.. :)

    but can you provide code for mouse wheel listener as well.. I want to get notified whenever mouse wheel is moved..

    Thanks in advance.

    Raj

  4. Raja says:

    Kris,

    I should mention again, that’s really amazing work.

    Really thanks.

    But as posted earlier, I am in urgent need of Mouse Wheel Listener..

    Please help me. (hereisrajiv@gmail.com)

    Thanks,
    Raja M

    • Hello Raja,

      I am verry happy to announce that I just released version 2.0 of the library, featuring a mouseWheel event for the GlobalMouseListener. Please find the code on GitHub.

      Hope this helps. Regards,
      Kristian

  5. my website is upcoming and I want to trace the ALT key pressed event using your api, please provide me a way.

  6. mehmet özkan says:

    Exception in thread “main” java.lang.UnsatisfiedLinkError: lib: keyboardhook-windows-amd64.dll not found in lib directory
    at lc.kra.system.LibraryLoader.loadLibrary(LibraryLoader.java:68)
    at lc.kra.system.keyboard.GlobalKeyboardHook.(GlobalKeyboardHook.java:86)
    at keyLogger.GlobalKeyboardExample.main(GlobalKeyboardExample.java:11)

    • Hello Mehmet, when does the exception occour? And how do you consume the library (Maven, downloaded the JAR from GitHub, as part of another JAR, etc.).

  7. Chuck says:

    Hi Kristian, thanks for this project! Was wondering if you had a timeline for mac/unix support? Thanks

    • Hey Chuck, providing support for Mac / Unix requires a lot of effort. Unfortunately I don’t think I will be able to implement it in near future. I have plans to do so, but don’t expect it in the next couple of month.

      Sorry & regards, Kristian

  8. As soon as my java application starts, I received this exception, please help..why i am receiving this…

    Exception in thread “Thread-7” java.lang.RuntimeException: Low-level keyboard hook failed (-2)
    at lc.kra.system.keyboard.GlobalKeyboardHook$NativeKeyboardHook.(GlobalKeyboardHook.java:169)
    at lc.kra.system.keyboard.GlobalKeyboardHook$2.(GlobalKeyboardHook.java:89)
    at lc.kra.system.keyboard.GlobalKeyboardHook.(GlobalKeyboardHook.java:89)
    at com.whodesire.kratis.Kratiso$9.run(Kratiso.java:808)
    at java.lang.Thread.run(Thread.java:745)

    • Hello Mohammed, this error occours if the low-level hooking mechanism fails. For Windows it is SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hInst, 0);. This is most likely the case if your user does not have the authorizations to do so, if there are no more hook handles available, or if you are running on an Windows release pre 2000. Which JVM / operating system are you using?

    • Windows 8.1 x64bit, JVM JDK 1.7

    • Hmm, so from a system spec. the system hook should be possible. Still could be an authorization error. Are you executing the JVM as an administrator? To figure out why the hook is failing do the following. Download the debug artifact JAR for your architecture from Appveyor. Use the JAR file to run your code. On standard out, you should see some new “NATIVE:” messages. If hooking fails, you should get a debug output why it fails. Please post your full console output here.

      Hope this helps. Regards, Kristian

  9. See, first 10-11 attempts are good in working of keyboard hooking certainly after it got failed & shows that the hook failed.

    • Hello Mohammad, as I said in my previous comment. To find out the root cause, you will have to use the debug JARs, which will provide an extended log output on standard out. Other than that, if hooking succeeds 10-11 times and fails afterwards, actually my first assumtion was right already that […] there are no more hook handles available […]. My guess is, you don’t shut down the hook properly by calling shutdownHook() of the hooking class. This happens, e.g. if you forcibly kill the JVM or simply never call shutdownHook(). In that case all hook handles are still occupied and the hooking fails on the 10th or 11th try.

  10. Francesco says:

    Hi,
    This is the best work in this direction. Great, great, great!
    I’ve a question: can I capture the selected text on a page, document in an external application?

    • Hello Francesco, glad you like it!

      Well the purpose of this library is to provide a low-level keyboard and mouse hook. The system hook is able to capture keyboard and mouse inputs, so everything which you type on your keyboard and every mouse movement. Going for your text selection example, the mouse hook would be able to capture the mouse button press and drag for the text selection, but it won’t be able to recognize a “text selection”, because it’s just for the low-level input.

      Hope this helps anyways. Regards, Kristian

    • Hey Man, Happy to listen those words, you are right, I am not calling the ShutdownHook thing and forcibly shutting my java application, and that the reason of getting crashed or not getting handler due to busy.

  11. jack says:

    How do I get the examples to work? Where do the DLLs come from? This is confusing as shit you need to make a step by step guide.

    • Hello Jack, sorry for the confusion. Let me try to put it simple:

      1. Download the latest version of the library from the releases section on GitHub. The DLLs for Windows 32- and 64-bit are already included in the JAR file you download.
      2. Add the JAR file to your project / add it to your Java Build/Class Path.
      3. Copy one of the examples from the GitHub page to your main method and you should be good to go.

      Hope this helps. Regards, Kristian

  12. Albert says:

    Hello Kristian,

    Fantastic job. I tried using the code but unable to compile the code and produce .class files.

    As in previous post I have placed the jar file CLASSPATH dir and complied it.

    cmd>java GlobalKeyboardExample.java

    KeyboardExample.java:24: error: cannot find symbol
    import lc.kra.system.keyboard.GlobalKeyboardHook;
    ^
    symbol: class GlobalKeyboardHook
    location: package lc.kra.system.keyboard
    C:\raju\hook\system-hook-2.0\src\main\java\lc\kra\system\keyboard\example\Global
    KeyboardExample.java:25: error: package lc.kra.system.keyboard.event does not ex
    ist
    import lc.kra.system.keyboard.event.GlobalKeyAdapter;
    ^
    C:\raju\hook\system-hook-2.0\src\main\java\lc\kra\system\keyboard\example\Global
    KeyboardExample.java:26: error: package lc.kra.system.keyboard.event does not ex
    ist
    import lc.kra.system.keyboard.event.GlobalKeyEvent;
    ^
    C:\raju\hook\system-hook-2.0\src\main\java\lc\kra\system\keyboard\example\Global
    KeyboardExample.java:32: error: cannot find symbol
    GlobalKeyboardHook keyboardHook = new GlobalKeyboardHook();
    ^
    symbol: class GlobalKeyboardHook
    location: class GlobalKeyboardExample
    C:\raju\hook\system-hook-2.0\src\main\java\lc\kra\system\keyboard\example\Global
    KeyboardExample.java:32: error: cannot find symbol
    GlobalKeyboardHook keyboardHook = new GlobalKeyboardHook();
    ^
    symbol: class GlobalKeyboardHook
    location: class GlobalKeyboardExample
    C:\raju\hook\system-hook-2.0\src\main\java\lc\kra\system\keyboard\example\Global
    KeyboardExample.java:35: error: cannot find symbol
    keyboardHook.addKeyListener(new GlobalKeyAdapter() {
    ^
    symbol: class GlobalKeyAdapter
    location: class GlobalKeyboardExample
    6 errors

    Can you provide in details how to use the proj.

    Thanks.

    • Hello Albert, from your log output it seems, that the system hook JAR is actually not part of your CLASSPATH. Try the following. Place the system-hook-<version>.jar together with the example .java file into any same folder. To compile the (keyboard) example, navigate to the folder and execute:

      javac -cp system-hook-<version>.jar GlobalKeyboardExample.java

      Before compiling, I actually recommend you to remove the package declaration, from the start of the GlobalKeyboardExample file. After compiling you can then execute the example with:

      java -cp ".;system-hook-<version>.jar" GlobalKeyboardExample

      If everything succeeded you should see the following console output:

      Global keyboard hook successfully started, press [escape] key to shutdown.

      Hope this helps. Regards, Kristian

    • Albert says:

      Hey Thanks. It worked after placing the jar file in the same directory.

      I see that acsi numbers are printed on the command console. Is there a way they can be converted into alphabets symbols and redirect then to some test file logging.

      Thanks.

    • Hello Albert, please have a look at the GlobalKeyEvent interface. But you have to be aware that you are using a low-level keyboard hook. Which means, the numbers you see on the console aren’t ACSII numbers, they are virtual key codes. Please find a list of virtual key codes in the GlobalKeyEvent interface. This means basically, that the physical keys on your keyboard is mapped. For so called “dead keys” you wont get the appropriate ACSII value, e.g. the “1” key is mapped to VK_1, but also if you press SHIFT+1, it’s mapped to VK_1. However the ASCII value for the key can be received from the GlobalKeyEvent by calling getKeyChar.

      To pipe the response into a log file, you can for instance use a FileWriter. Hope this helps. Regards, Kristian