java - 使用 JNI 时如何在 C++ 中获取 Java 异常的描述?

标签 java c++ java-native-interface

我想确定在使用 JNI 从 C++ 代码调用该函数时 Java 函数引发了什么异常。我有以下代码可以捕获 Java 异常:

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();
}

我不知道如何在此 C++ 代码中获取有关 Java 异常的描述性信息。有人可以帮忙吗?

最佳答案

为了简洁起见,我省略了在每次 JNI 调用后调用 ExceptionCheck() 并检查是否有任何失败的尝试定位方法:您应该在实现时添加这些。

首先,存储异常,然后获取获取Throwable信息所需的Java方法:

// 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;");

其次,递归构造错误信息(你可能要修改这个):

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);
        }
    }
}

我从几年前编写的代码中复制了这个(修改以消除样板 ExceptionCheck()s),但我没有编译我发布的内容,但一般方法希望很清楚。

关于java - 使用 JNI 时如何在 C++ 中获取 Java 异常的描述?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10408972/

相关文章:

java - JPA 引用表映射

Android - 使用 jni 将波形文件的 channel 从立体声更改为单声道或从单声道更改为立体声

c++ - 观察者设计模式接口(interface)契约设计问题

c++ - 通过将模板参数与字符串连接生成 C++ 类型名

java - Android:从 NativeActivity 使用 JNI

java - JNI JVM 调用类路径

java - 为什么我的 Java9 模块服务不工作?

java - 数组搜索代码

java - 更改 Lotus Notes 中的 "from"字段

c++ - 使用 vbscript 部署 C++ 程序?