what is wrong with this call to the java method?

2020-02-11 05:47发布

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

3条回答
男人必须洒脱
2楼-- · 2020-02-11 06:10

I believe you cannot call a java method that takes a String parameter and pass it a char*. You should call NewStringUTF first.

查看更多
聊天终结者
3楼-- · 2020-02-11 06:15

I think it is due to the UAC feature enabled on your Operating System. This was a bug for Java 6. Read this for further reference.

The reason I say this is because the event to the escape key is fired correctly and the problem only begins as soon as the call to the java method is done.

查看更多
Ridiculous、
4楼-- · 2020-02-11 06:18

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.

查看更多
登录 后发表回答