I am writing a program in Java that is supposed to recognize and respond to global input, i.e. keyboard and mouse events, even when the form is not in focus, which requires keyboard and mouse hooks for Windows using the Java Native Interface. I have created the DLL from C++ code posted on the Sun forums in a topic about keyboard hooks and tried using it in conjunction with Java code also posted there. The code is as follows:

C++:
Code:
// syshook.cpp
/*
 *	SysHook - 7/17/05
 *	Jacob Gohlke
 *	
 *	JNI Interface for setting a Keyboard Hook and monitoring
 *	it Java-side
 *
 *	(c) Copyright 2005 Jacob Gohlke
 *	
 *	Feel free to use and learn from this code, royalty-free!
 *	I only ask you acknkowlege what library you are using
 *	and who made it. Thanks, and happy hooking!
 */
 
#include <windows.h>
#include <winuser.h>
#include "jni.h"
#include "syshook.h"
 
#pragma data_seg(".HOOKDATA") //Shared data among all instances.
static HHOOK hkb = NULL;
static HANDLE g_hModule = NULL;
static WPARAM g_wParam = NULL;
static LPARAM g_lParam = NULL;
 
JNIEXPORT void NotifyJava(JNIEnv *env, jobject obj, WPARAM wParam, LPARAM lParam)
{		
	jclass cls = env->GetObjectClass(obj);
	jmethodID mid;
 
	mid = env->GetMethodID(cls, "Callback", "(ZIZZ)V");
	if (mid == NULL) 
			return;
 
	if( (HIWORD( lParam ) & KF_UP) )
		env->CallVoidMethod(obj, mid, (jboolean)FALSE, (jint)(wParam), (jboolean)(HIWORD( lParam ) & KF_ALTDOWN), (jboolean)(HIWORD( lParam ) & KF_EXTENDED));
	else
		env->CallVoidMethod(obj, mid, (jboolean)TRUE, (jint)(wParam), (jboolean)(HIWORD( lParam ) & KF_ALTDOWN), (jboolean)(HIWORD( lParam ) & KF_EXTENDED));
}
 
#pragma data_seg() 
 
#pragma comment(linker, "/SECTION:.HOOKDATA,RWS")
 
JNIEXPORT LRESULT CALLBACK HookKeyboardProc(INT nCode, WPARAM wParam, LPARAM lParam) 
{
    if (nCode < 0)  // do not process message 
		return CallNextHookEx(hkb, nCode, wParam, lParam); 
 
	g_wParam = wParam;
	g_lParam = lParam;
	return CallNextHookEx(hkb, nCode, wParam, lParam);
} 
 
JNIEXPORT void JNICALL Java_PollThread_checkKeyboardChanges(JNIEnv *env, jobject obj)
{
	if(g_wParam != NULL && g_lParam != NULL)
	{
		NotifyJava(env, obj, g_wParam, g_lParam);
		g_wParam = NULL;
		g_lParam = NULL;
	}
}
 
static void Init()
{
	hkb = SetWindowsHookEx( WH_KEYBOARD, (HOOKPROC)HookKeyboardProc, (HINSTANCE)g_hModule, 0 );
}
 
static void Cleanup()
{
	if( hkb != NULL )
		UnhookWindowsHookEx( hkb );
 
	hkb = NULL;
}
 
BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved )
{
	switch(ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
			g_hModule = hModule;
			Init();
			return TRUE;
 
		case DLL_PROCESS_DETACH:
			Cleanup();
			return TRUE;
	}
 
    return TRUE;
}
Code:
// syshook.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class PollThread */
 
#ifndef _Included_PollThread
#define _Included_PollThread
#ifdef __cplusplus
extern "C" {
#endif
#undef PollThread_MIN_PRIORITY
#define PollThread_MIN_PRIORITY 1L
#undef PollThread_NORM_PRIORITY
#define PollThread_NORM_PRIORITY 5L
#undef PollThread_MAX_PRIORITY
#define PollThread_MAX_PRIORITY 10L
/*
 * Class:     PollThread
 * Method:    checkKeyboardChanges
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_PollThread_checkKeyboardChanges
  (JNIEnv *, jobject);
 
#ifdef __cplusplus
}
#endif
#endif
JAVA:
Code:
//KeyboardEvent.java
import java.util.*;
 
public class KeyboardEvent extends EventObject
{
	boolean ts, ap, ek;
	int vk;
 
	public KeyboardEvent( Object source, boolean ts, int vk, boolean ap, boolean ek )
	{
		super(source);
		this.ts = ts;
		this.vk = vk;
		this.ap = ap;
		this.ek = ek;
	}
 
	public boolean getTransitionState()
	{
		return ts;
	}
 
	public long getVirtualKeyCode()
	{
		return vk;
	}
 
	public boolean isAltPressed()
	{
		return ap;
	}
 
	public boolean isExtendedKey()
	{
		return ek;
	}
 
	public boolean equals( KeyboardEvent event )
	{
		if( event.getVirtualKeyCode() == vk )
		{
			if( event.isExtendedKey() == ek )
			{
				if( event.isAltPressed() == ap )
				{
					return true;
				}
			}
		}
		return false;
	}
}
Code:
// KeyboardEventListener.java
import java.util.*;
 
public interface KeyboardEventListener extends EventListener
{
	public void GlobalKeyPressed( KeyboardEvent event );
	public void GlobalKeyReleased( KeyboardEvent event );
}
Code:
// KeyboardHook.java
import java.io.*;
 
public class KeyboardHook
{
	public KeyboardHook()
	{
		( new PollThread(this) ).start();
	}
 
	protected javax.swing.event.EventListenerList listenerList = new javax.swing.event.EventListenerList();
 
	public void addEventListener(KeyboardEventListener listener)
	{
		listenerList.add( KeyboardEventListener.class, listener );
	}
 
	public void removeEventListener(KeyboardEventListener listener)
	{
		listenerList.remove( KeyboardEventListener.class, listener );
	}
 
	void keyPressed(KeyboardEvent event)
	{
		Object[] listeners = listenerList.getListenerList();
		for ( int i = 0; i < listeners.length; i += 2 )
		{
			if ( listeners[ i ] == KeyboardEventListener.class )
			{
				( (KeyboardEventListener)listeners[i + 1] ).GlobalKeyPressed( event );
			}
		}
	}
 
	void keyReleased(KeyboardEvent event)
	{
		Object[] listeners = listenerList.getListenerList();
		for ( int i = 0; i < listeners.length; i += 2 )
		{
			if ( listeners[ i ] == KeyboardEventListener.class )
			{
				( (KeyboardEventListener)listeners[i + 1] ).GlobalKeyReleased( event );
			}
		}
	}
}
 
class PollThread extends Thread
{
	public native void checkKeyboardChanges();
	private KeyboardHook kbh;
 
	public PollThread( KeyboardHook kh )
	{
		kbh = kh;
		System.loadLibrary("syshook");
	}
 
	public void run()
	{
		for(;;)
		{
			checkKeyboardChanges();
			yield();
		}
	}
 
	void Callback( boolean ts, int vk, boolean ap, boolean ek )
	{
		KeyboardEvent event = new KeyboardEvent( this, ts, vk, ap, ek );
		if( ts )
		{
			kbh.keyPressed( event );
		}
		else
		{
			kbh.keyReleased( event );
		}
	}
}
Example in Java:
Code:
// Test.java
import java.io.*;
 
public class Test implements KeyboardEventListener
{
	public static void main( String args[] )
	{
		KeyboardHook kh = new KeyboardHook();
		kh.addEventListener( new Test() );
		BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) );
		try{
			br.readLine();
		} catch( IOException ex ) {}
	}
 
	public void GlobalKeyPressed( KeyboardEvent event )
	{
		System.out.println( "Key Pressed: " + event.getVirtualKeyCode() );
	}
 
	public void GlobalKeyReleased( KeyboardEvent event )
	{
		System.out.println( "Key Released: " + event.getVirtualKeyCode() );
	}
}
The code was originally posted at http://forums.sun.com/thread.jspa?threadID=632369.

From what I understand, this is all the code needed to capture global key events. However, when I run the program, the following error occurs:

Code:
Exception in thread "Thread-0" java.lang.UnsatisfiedLinkError: x.keyboardHookTest.PollThread.checkKeyboardChanges()V
	at x.keyboardHookTest.PollThread.checkKeyboardChanges(Native Method)
	at x.keyboardHookTest.PollThread.run(KeyboardHook.java:52)
Note: line 52 of KeyboardHook.java refers to the method checkKeyboardChanges().

What is going on with the program?