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 exception:
JNIEnv * pEnv; // assume this is properly initialized jclass javaClass; // ditto jmethodID javaFunction; // ditto pEnv->CallStaticVoidMethod(javaClass, javaFunction); jthrowable exc; if(exc = pEnv->ExceptionOccurred()) { pEnv->ExceptionClear(); }
I do not know how to obtain descriptive information about the Java exception within this C++ code. Can somebody help?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With