How to obtain a description of a Java exception in C++ when using JNI?

前端 未结 3 530
慢半拍i
慢半拍i 2021-02-02 12:30

I would like to determine what exception has been thrown by a Java function when calling that function from C++ code using JNI. I have the following code that catches the Java

相关标签:
3条回答
  • 2021-02-02 13:08

    If you are just interested in the stack trace of the exception you can:

    if (env->ExceptionOccurred()) // check if an exception occurred
    {
        env->ExceptionDescribe(); // print the stack trace          
    }
    
    0 讨论(0)
  • 2021-02-02 13:17

    The easy way out of this is to declare the JNI method to throw all possible exceptions, then:

    jthrowable throwable = ExceptionOccurred(env);
    if (throwable != NULL)
       Throw(env, throwable);
    

    and let your Java code deal with it.

    0 讨论(0)
  • 2021-02-02 13:30

    I have omitted calling ExceptionCheck() after each JNI call and checking for any failed attempts to locate methods for brevity: you should add these when implementing.

    First, store the exception and then acquire the Java methods necessary for obtaining information about the Throwable:

    // Get the exception and clear as no
    // JNI calls can be made while an exception exists.
    jthrowable exception = pEnv->ExceptionOccurred();
    pEnv->ExceptionClear();
    
    jclass throwable_class = pEnv->FindClass("java/lang/Throwable");
    jmethodID mid_throwable_getCause =
        pEnv->GetMethodID(throwable_class,
                          "getCause",
                          "()Ljava/lang/Throwable;");
    jmethodID mid_throwable_getStackTrace =
        pEnv->GetMethodID(throwable_class,
                          "getStackTrace",
                          "()[Ljava/lang/StackTraceElement;");
    jmethodID mid_throwable_toString =
        pEnv->GetMethodID(throwable_class,
                          "toString",
                          "()Ljava/lang/String;");
    
    jclass frame_class = pEnv->FindClass("java/lang/StackTraceElement");
    jmethodID mid_frame_toString =
        pEnv->GetMethodID(frame_class,
                          "toString",
                          "()Ljava/lang/String;");
    

    Second, recursively construct the error message (you may want to modify this):

    std::string error_msg; // Could use ostringstream instead.
    
    _append_exception_trace_messages(*pEnv,
                                     error_msg,
                                     exception,
                                     mid_throwable_getCause,
                                     mid_throwable_getStackTrace,
                                     mid_throwable_toString,
                                     mid_frame_toString);
    
    void _append_exception_trace_messages(
                            JNIEnv&      a_jni_env,
                            std::string& a_error_msg,
                            jthrowable   a_exception,
                            jmethodID    a_mid_throwable_getCause,
                            jmethodID    a_mid_throwable_getStackTrace,
                            jmethodID    a_mid_throwable_toString,
                            jmethodID    a_mid_frame_toString)
    {
        // Get the array of StackTraceElements.
        jobjectArray frames =
            (jobjectArray) a_jni_env.CallObjectMethod(
                                            a_exception,
                                            a_mid_throwable_getStackTrace);
        jsize frames_length = a_jni_env.GetArrayLength(frames);
    
        // Add Throwable.toString() before descending
        // stack trace messages.
        if (0 != frames)
        {
            jstring msg_obj =
                (jstring) a_jni_env.CallObjectMethod(a_exception,
                                                     a_mid_throwable_toString);
            const char* msg_str = a_jni_env.GetStringUTFChars(msg_obj, 0);
    
            // If this is not the top-of-the-trace then
            // this is a cause.
            if (!a_error_msg.empty())
            {
                a_error_msg += "\nCaused by: ";
                a_error_msg += msg_str;
            }
            else
            {
                a_error_msg = msg_str;
            }
    
            a_jni_env.ReleaseStringUTFChars(msg_obj, msg_str);
            a_jni_env.DeleteLocalRef(msg_obj);
        }
    
        // Append stack trace messages if there are any.
        if (frames_length > 0)
        {
            jsize i = 0;
            for (i = 0; i < frames_length; i++)
            {
                // Get the string returned from the 'toString()'
                // method of the next frame and append it to
                // the error message.
                jobject frame = a_jni_env.GetObjectArrayElement(frames, i);
                jstring msg_obj =
                    (jstring) a_jni_env.CallObjectMethod(frame,
                                                         a_mid_frame_toString);
    
                const char* msg_str = a_jni_env.GetStringUTFChars(msg_obj, 0);
    
                a_error_msg += "\n    ";
                a_error_msg += msg_str;
    
                a_jni_env.ReleaseStringUTFChars(msg_obj, msg_str);
                a_jni_env.DeleteLocalRef(msg_obj);
                a_jni_env.DeleteLocalRef(frame);
            }
        }
    
        // If 'a_exception' has a cause then append the
        // stack trace messages from the cause.
        if (0 != frames)
        {
            jthrowable cause = 
                (jthrowable) a_jni_env.CallObjectMethod(
                                a_exception,
                                a_mid_throwable_getCause);
            if (0 != cause)
            {
                _append_exception_trace_messages(a_jni_env,
                                                 a_error_msg, 
                                                 cause,
                                                 a_mid_throwable_getCause,
                                                 a_mid_throwable_getStackTrace,
                                                 a_mid_throwable_toString,
                                                 a_mid_frame_toString);
            }
        }
    }
    

    I copied this from code I wrote a few years ago (modified to eliminate boiler plate ExceptionCheck()s), but I did not compile what I have posted but the general approach is hopefully clear.

    0 讨论(0)
提交回复
热议问题