B4J Library jNativeHookB4J for intercepting system input events

Generally speaking, a hook is a method of intercepting program events without a dedicated API. You can read more about it here: https://en.wikipedia.org/wiki/Hooking

This library wraps the JNativeHook library, allowing you to intercept global keyboard events and mouse events. That is, your B4J application can now recieve these events even if they occur outside your application or your application is not in focus or has no GUI. In order to use this library, download the JNativeHook-<version>.zip file from this address: https://github.com/kwhat/jnativehook/releases . The B4J library was written for version 2.0.2. Unzip the archive and place the JNativeHook.jar file in your B4J external libraries folder. Then download the jNativeHookB4J.zip file attached to this post and extract the jNativeHookB4J.jar and jNativeHookB4J.xml files to your B4J external libraries folder.

The following code shows an example of its usage:
B4X:
Sub Process_Globals
    Dim NH as NativeHook
    Dim t as Timer
End Sub

Sub AppStart(Args() as String)
    NH.Initialize("NH", Me)        'Initializes and registers the NativeHook
    NH.startNativeKeyListener    'starts a NativeKeyListener
    NH.enableKillCode   'Allows you to manually unregister by pressing Windows_Escape
    NH.EnableEventConsumption  'Allows you to consume input events
    t.Initialize("t", 60*1000)
    t.Enabled = True            'Timer will unregister the NativeHook in 1 minute
    StartMessageLoop
End Sub

'Event fired whenever a key is pressed, regardless of what application is in focus
Sub NH_NativeKeyPressed(nke as NativeKeyEvent) As Boolean
    If NH.isCharacter(nke.keyCode) Then
        Log(nke.keyText & " Pressed")
        If nke.keyText=="F" Then Return True    'Consumes the event if the user pressed the "F" key
    End If
    Return False
End Sub

'Event fired when the NativeHook is unregistered
Sub NH_Unregistered
    Log("NativeHook unregistered")
    StopMessageLoop    'No more event loops means this program ends here
End Sub

Sub t_Tick
    t.Enabled = False
    NH.unregisterNativeHook
End Sub

Note how in the above example, all the NH_ events appear in the Main module. This doesn't mean, however, that they will execute in the Main thread. They will execute in the JNativeHook delegate thread. This is a general advantage however it means that if you want to access UI elements from within your event subs, you will have to use Thread.RunOnGuiThread() to do so. Also note that unregistering the NativeHook will end the NativeHook event loop. If this was the only active event loop in your program (no UI event loop, no server event loop, etc...), ending that event loop will end your program. That is why an _Unregistered event is provided.

Make sure to unregister the NativeHook when your program is done using it. If too many NativeHooks are left registered but unused, this will interfere with your ability to create new instances of NativeHook and for other programs that respond to these system events to do so. If this happens, reboot your computer and it should clear all those dangling NativeHooks from memory. However, if you properly unregister your NativeHooks, you shouldn't have any problems.

My preliminary tests with this library indicate that it performs very well and consumes very little computing resources. On a few years old i7 system, a program that I wrote with this library stayed at around 0.03% CPU load and very rarely spiked to 0.5% even with heavy mouse and keyboard usage and all native listeners running. The B4J library is pretty heavily commented and the comments sort of lead you through its usage. The JNativeHook.jar file that you download from github has packed into it all the native files necessary to work on Windows, Linux and Darwin-based Mac systems. I've only tested it on Windows, however.

EDIT (4JUL2015): jNativeHookB4J.zip was compiled against jdk 8u40. I've attached another .zip called jNativeHookB4JAncient.zip that was compiled against jdk 6u26 which should work for users and customers still using Javas 6 or 7 (I've only tested Ancient on jdk 7u71, though). jNativeHookB4jAncient works in exactly the same way except the NativeHook object is called NativeHookAncient. If you're using Java 5 or below, well, you're on your own...

EDIT (5JUL2015): Updated both libraries to version 1.1 to enable event consumption. See post #18 for details.
 

Attachments

  • jNativeHookB4J1.1.zip
    8.6 KB · Views: 1,254
  • jNativeHookB4JAncient1.1.zip
    8.7 KB · Views: 1,020
Last edited:

Roycefer

Well-Known Member
Licensed User
Longtime User
This library works in UI apps. However, you won't be able to directly access UI elements from the NativeHook events since they run in a separate thread. Instead, you'll have to use Thread.RunOnGuiThread() to access UI elements (from the Threading library). I don't think this is your problem, though.

Are you sure you put the JNativeHook 2.0.2 jar in your B4J external libraries folder (not the B4A one)? What computer and OS are you using?
 

Suntzu

Member
Licensed User
Longtime User
Yes, JNativeHook 2.0.2 jar is in the B4J external libraries folder. I'm on a Sony VAIO laptop, Windows Vista Home SP2.
 

Roycefer

Well-Known Member
Licensed User
Longtime User
Were you able to run any of the sample code from this thread in a non-UI app? Also note that sometimes your anti-virus may attempt to block this library (for obvious reasons).
 

Suntzu

Member
Licensed User
Longtime User
I tried the example from your original post on a barebone project and got the same error.

Good idea. I will exclude the jar and related files in my antivirus.

Thank you.
 

MbedAndroid

Well-Known Member
Licensed User
Longtime User
trying this tool i encounter a build error line 327 RobotDelay
Any idea?
B4J version: 3.71
Parsing code. (0.01s)
Compiling code. Error
Error compiling program.
Error description: Unknown member: robotdelay
Occurred on line: 327
lore.RobotDelay(10)
Word: robotdelay

Edit: just changed the provided libs, problem solved!
 
Last edited:

wonder

Expert
Licensed User
Longtime User
Hello!

I'd like to use the NativeMouseWheelEvent, but I don't know how.
Can someone post an example?
 

Roycefer

Well-Known Member
Licensed User
Longtime User
The NativeMouseWheelEvent object is passed to the _NativeMouseWheelMoved() event sub as an argument, holding all the pertinent info about the mouse wheel event (amount, direction, etc... all available in the code-completion menu). You shouldn't be trying to create objects of type NativeMouseWheelEvent.
B4X:
Sub nh_NativeMouseWheelMoved(nmwe As NativeMouseWheelEvent) As Boolean
    Log("Scroll Type: " & nmwe.ScrollType)
    Log("Scroll Amount: " & nmwe.ScrollAmount)
    Log("Wheel Rotation: " & nmwe.WheelRotation)
    Return False
End Sub
Obviously, you need to start the appropriate Listener with nh.startNativeMouseWheelListener().

As usual, don't forget to unregister your NativeHook when you're done using it.
 

Hanstel

Member
Licensed User
is there a similar Library for web application? or taking system event from PC/Laptop Webcam aside from keyboard and mouse?
 

Roycefer

Well-Known Member
Licensed User
Longtime User
There's an incomplete webcam library somewhere in the libraries forum. A search should reveal it. The problem is that there aren't very many good, complete Java webcam libraries to wrap. Anyhow, that's a topic entirely separate from this thread.

As for intercepting system input events in web applications, do you mean in the browser via a JS API? I don't know of any standard JavaScript APIs that expose system input events through the browser. And it seems like doing so would be a yuge security violation. It might be possible using ActiveX or some other browser plugins. I'm not entirely sure.
 

Hanstel

Member
Licensed User
yes at least on the browser? how do you wrap an existing Javascript (like keydrown), still newbie and learning b4j
 

Roycefer

Well-Known Member
Licensed User
Longtime User
You should study some of the WebSocket examples and familiarize yourself with some JavaScript and JQuery. Once you get good with those, you'll be able to have your B4J server run arbitrary JavaScript code in the client browser via WebSocket.Eval() and related methods. One doesn't really "wrap" JavaScript libraries in B4J. But if the HTML document includes a <script> reference to a JS library, you could package a bunch of calls to that JS library using WebSocket.Eval() into a B4J class and compile it into a library.
 

JakeBullet70

Well-Known Member
Licensed User
Longtime User
Hi @Roycefer

I got everything working but am trying to tram CTRL and SHIFT too.

B4X:
Sub NH_NativeKeyPressed(nke As NativeKeyEvent) As Boolean
    Dim Shift As Int = 16
    Dim Ctrl As Int = 17
    If NH.isCharacter(nke.keyCode) And NH.isSpecialKey(Shift) And NH.isSpecialKey(Ctrl) Then
        Log(nke.keyText & " Pressed")
        If nke.keyText=="Z" Then
            Log("Got it!")
            Return True    'Consumes the event if the user pressed the "F" key
        End If
    End If
    Return False

End Sub

What am I missing? If I just grab the key it is fine.

Thanks.
 
Last edited:

Roycefer

Well-Known Member
Licensed User
Longtime User
It looks like you are trying to identify when the user uses the ctrl_shift_f key combo. Is that correct?

If so, I recommend doing the following. Create Process_Global Boolean variables called ShiftDown and CtrlDown (both initially set to False). Set them to True inside NH_NativeKeyPressed if the NativeKeyEvent indicates the corresponding key was pressed with code that looks something like this:
B4X:
If nke.KeyText.ToLowerCase.Contains("control") Then CtrlDown = True    'this catches both Left Control and Right Control. Do the same with the Shift keys.
Don't forget to do the inverse in NH_NativeKeyReleased. Then, later on inside NH_NativeKeyPressed, you can check if the F key is pressed and if it is, you can check to see if CtrlDown and ShiftDown are both true. If so, then you know ctrl_shift_f was entered by the user.

Remember that each key press with generate its own Event. A 3-key combo like ctrl_shift_f will generate three events so we use Process_Global variables to communicate state between them.
 

JakeBullet70

Well-Known Member
Licensed User
Longtime User
Running into this error but ONLY with the NH_NativeKeyReleased sub. If I remove it the all is good.


upload_2016-10-8_10-51-11.png
 

Roycefer

Well-Known Member
Licensed User
Longtime User
It looks like you have the event signature wrong. There should be a return value "As Boolean" and then, inside the event sub, you should specify a Boolean return value.
 

skeedsr

Member
hy everyone

i tryed the example in the first page but i recive this error while compiling

B4J version: 4.20 (1)
Parsing code. (0.00s)
Compiling code. (0.01s)
Compiling layouts code. (0.00s)
Compiling generated Java code. Error
B4J line: 8
NH.Initialize(\
javac 1.8.0_91
src\b4j\example\main.java:48: error: cannot access NativeKeyListener
_nh.Initialize("NH",main.getObject());
^
class file for org.jnativehook.keyboard.NativeKeyListener not found
1 error

some question

where i can find the file jnativehook 2.0.2 coz in the first page there is only 1.0.x

in my b4j i have not the folder named external library but i have Libraries and add libraries

thanks

livio
 
Top