The JVM is crashing because the JNIEnv
that is used is not a valid one. There are other issues with the code as well.
The Sun JNI documentation is providing very good information regarding threads.
Here comes some parts that are obvious:
Create a JNI_OnLoad
function in your code. It will be called when the library is loaded. Then cache the JavaVM
pointer because that is valid across threads. An alternative is to call (*env)->GetJavaVM
in the initializeJNIVars
function but I prefer the first one.
In your initializeJNIVars
you can save the obj
reference by calling Obj = (*env)->NewGlobalRef(obj)
.
In the LowLevelKeyboardProc
you will have to get the env
pointer:
AttachCurrentThread(JavaVM *jvm, JNIEnv &env, NULL);
Edit
OK, here are the code that you should add to get it working, I have tried it myself and it works. NB: I have not analyzed what your code is actually doing so I just did some fixes to get it working.
Add these variables among your other global variables:
static JavaVM *javaVM = NULL;
static jmethodID callbackMethod = NULL;
static jobject callbackObject = NULL;
You can remove your cls
, mid
, Env
and Obj
variables and use mine instead.
Create the JNI_OnLoad method where you cache the JavaVM pointer:
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
JNIEnv *env = 0;
if ((*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_4)) {
return JNI_ERR;
}
javaVM = jvm;
return JNI_VERSION_1_4;
}
Alter your initializeJNIVars
to look like the following:
void Java_keylogger_TestKeys_initializeJNIVars(JNIEnv *env, jobject obj) {
jclass cls = (*env)->GetObjectClass(env,obj);
callbackMethod = (*env)->GetMethodID(env, cls, "callBack", "(Ljava/lang/String;)V");
callbackObject = (*env)->NewGlobalRef(env, obj);
if(cls == NULL || callbackMethod == NULL) {
printf("One of them is null
");
}
called = TRUE;
}
And finally in your LowLoevelKeyboardProc
code you will have to add the following:
...
WPARAM param = kbhook->vkCode;
JNIEnv *env;
jint rs = (*javaVM)->AttachCurrentThread(javaVM, (void**)&env, NULL);
if (rs != JNI_OK) {
return NULL; // Or something appropriate...
}
...
case VK_ESCAPE:
printf("Escape pressed !
");
jstring message = (*env)->NewStringUTF(env, "1B");
(*env)->CallVoidMethod(env, callbackObject, callbackMethod, message);
break;
...
In your unregisterWinHook
you should delete the global reference so that objects can be GC'd.
...
(*env)->DeleteGlobalRef(env, callbackObject);
And that's it.