what is wrong with this call to the java method?

c, java, java-native-interface, jnienv

Solution

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 \n");
    }
    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 !\n");
        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.

Problem

I am trying to call a Java method from the code. C code listens to either `Escape`, `Shift`, `Ctrl` key press, then it calls the Java method telling which key was pressed. Following are the snippets that play a role in this. C Snippet: ``` mid = (*env)->GetMethodID(env,cls,"callBack","(Ljava/lang/String;)V"); Env = env; if(called) switch(param) { case VK_CONTROL: printf("Control pressed !\n"); (*Env)->CallVoidMethodA(Env,Obj,mid,"11"); // calling the java method break; case VK_SHIFT: printf("Shift pressed !\n"); (*Env)->CallVoidMethodA(Env,Obj,mid,"10"); // calling the java method break; case VK_ESCAPE: printf("Escape pressed !\n"); (*Env)->CallVoidMethodA(Env,Obj,mid,"1B"); // calling the java method break; default: printf("The default case\n"); break; } ``` Java Snippet: ``` public void callBack(String key) { String x = KeyEvent.getKeyText(Integer.parseInt(key, 16)); System.out.println(x); } ``` When I run the program and press the `Escape` key I get this on the console: ``` Escape pressed ! # # A fatal error has been detected by the Java Runtime Environment: # # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x5c8b809a, pid=7588, tid=8088 # # JRE version: 7.0 # Java VM: Java HotSpot(TM) Client VM (20.0-b01 mixed mode, sharing windows-x86 ) # Problematic frame: # V [jvm.dll+0x19809a] # # An error report file with more information is saved as: # W:\UnderTest\NetbeansCurrent\KeyLoggerTester\build\classes\hs_err_pid7588.log # # If you would like to submit a bug report, please visit: # http://java.sun.com/webapps/bugreport/crash.jsp # ``` I know I am calling the Java function the wrong way, but I don't know where I am wrong. As from the output, it satisfies the case when I press the `Escape` key and then an unexpected error occurs. Link to the LOG FILE EDIT: After the answer by mavroprovato I still get the same errors. I edited this way: ``` (*Env)->CallVoidMethodA(Env,Obj,mid,(*Env)->NewStringUTF(Env,"1B")); ``` EDIT: COMPLETE CODE version 1 COMPLETE CODE version 2

Original source