0

I have a JNI-Interface between C++ and Java. I want to pass a callback function as a parameter in a function on java side. This will be called later in C ++ via JNI and should return me a string.

This is what I have:

Java:

instance.LoadFileFromPath(Object callbackObject);

The function for callbackObject is this:

public String CallbackTest (String str)
{
    //Log-Ausgabe
    System.out.println("CallbackString: " + str);
    return str;
}

This object is saved into a global variable in JNI:

jobject g_callbackObject;
jmethodID g_callbackMethod;
JNIEnv* g_env;

JNIEXPORT jboolean JNICALL xx_LoadFileFromPath (JNIEnv* env, jobject o, jobject callbackObject)
{
    jclass callBackClass = env->GetObjectClass(callbackObject);
    jmethodID callBackMethode = env->GetMethodID(callBackClass, "CallbackTest ", "(Ljava/lang/String;)Ljava/lang/String;");

    g_env = env;
    g_callbackObject = callbackObject;
    g_callbackMethod = callBackMethode;

    bool res = LoadFileFromPath(LoadFileFromPathCallbackWrapper);

    return res;
}

Later in C++ I want to call the Java-Function "CallbackTest":

char* LoadFileFromPathCallbackWrapper(char* fileName)
{
    jstring res = (jstring)g_env->CallObjectMethod(g_callbackObject, g_callbackMethod, g_env->NewStringUTF(fileName));
    const char* nativeStr = g_env->GetStringUTFChars(res, NULL);
#ifdef OS_LINUX
    char* result = strdup(nativeStr);
#else
    char* result = _strdup(nativeStr);
#endif
    g_env->ReleaseStringUTFChars(res, nativeStr);

    return result;
}

Everything looks okay, but g_env->CallObjectMethod returns NULL. The Log from CallbackTest in java is never printed.

I am not allowed to change the interface.

Does anyone have an idea, what could be the problem?

Thanks!

_____ Edit:

Thanks, the method name in jmethodID callBackMethode = env->GetMethodID() was wrong, because I had to change the names for privacy reasons. I edited the question. ExceptionCheck returns 0.

I tried the following now:

JavaVM* g_vm;
jobject g_callbackObject;
jmethodID g_callbackMethod;
//JNIEnv* g_env;
          

JNIEXPORT jboolean JNICALL xx_LoadFileFromPath (JNIEnv* env, jobject o, jobject callbackObject)
{
    env->GetJavaVM(&g_vm);
    jint version = env->GetVersion();
    jclass callBackClass = env->GetObjectClass(callbackObject);
    jboolean check = env->ExceptionCheck(); //returns 0
    jmethodID callBackMethode = env->GetMethodID(callBackClass, "CallbackTest", "(Ljava/lang/String;)Ljava/lang/String;");
    check = env->ExceptionCheck(); //returns 0;

    //g_env = env;
    g_callbackObject = callbackObject;
    g_callbackMethod = callBackMethode;

    bool res = LoadFileFromPath(LoadFileFromPathCallbackWrapper);

   return res;
}

bool GetJniEnv(JavaVM* vm, JNIEnv **env) {
    bool did_attach_thread = false;
    *env = nullptr;
    int getEnvStat = vm->GetEnv((void**)env, JNI_VERSION_1_6);

    if (getEnvStat == JNI_EDETACHED) {
        if (vm->AttachCurrentThread((void**)env, NULL) == JNI_OK) {
            did_attach_thread = true;
        }
        else {}
    }

    return did_attach_thread;
}

char* LoadFileFromPathCallbackWrapper(char* fileName)
{
    JNIEnv* g_env;
    GetJniEnv(g_vm, &g_env);
    jstring res = (jstring)g_env->CallObjectMethod(g_callbackObject, g_callbackMethod, g_env->NewStringUTF(fileName));
    jboolean check = g_env->ExceptionCheck(); **//returns 1**

    const char* nativeStr = g_env->GetStringUTFChars(res, NULL);
#ifdef OS_LINUX
    char* result = strdup(nativeStr);
#else
    char* result = _strdup(nativeStr);
#endif
    g_env->ReleaseStringUTFChars(res, nativeStr);

    return result;
}

It's still the same error... :(

TheValbo
  • 9
  • 3
  • `g_env = env;` <-- That looks suspicious, since you cannot use a particular `JNIEnv*` on other threads. See https://stackoverflow.com/questions/30026030/what-is-the-best-way-to-save-jnienv/30026231#30026231 – Michael May 20 '21 at 10:32
  • 1
    This, and `callBackMethode` is probably 0, because the method name is wrong. Always use `ExceptionCheck()`. – user2543253 May 20 '21 at 11:31
  • 1
    You can't save (1) `JNIEnv*` or (2) `jobjects` statically. See the JNI Specification. – user207421 May 20 '21 at 12:25
  • 1
    So your exception check in `LoadFileFromPathCallbackWrapper` returns true. That means there actually *is* an exception. See what it is. (Use `ExceptionDescribe()` for a quick look, use proper handling in production code) – user2543253 May 20 '21 at 12:29
  • It's an access violation: Exception thrown at 0x00007FFA41A1B714 (jvm.dll) in javaw.exe: 0xC0000005: Access violation reading location 0x0000000000000039. I tried to call `env->CallObjectMethod(callbackObject, callBackMethode, "HelloTest");` directly in LoadFileFromPath(). Same exception. My java class and method are both public. ExceptionCheck after `jclass callBackClass = env->GetObjectClass(callbackObject);` and `jmethodID callBackMethode = env->GetMethodID(callBackClass, "CallbackTest", "(Ljava/lang/String;)Ljava/lang/String;");` are both 0. – TheValbo May 20 '21 at 19:15
  • An access violation is not a Java exception. What does an `ExceptionDescribe` print after you call `CallObjectMethod` (assuming your code still looks like the above)? – user2543253 May 21 '21 at 09:59

0 Answers0