2012-05-02 44 views
13

JNIを使​​用してC++コードからその関数を呼び出すときにJava関数によってスローされた例外を特定したいと考えています。私はこのC++コード内のJavaの例外についての記述情報を取得する方法がわからないJNIを使​​用しているときにC++で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(); 
} 

:私は、Javaの例外をキャッチし、次のコードを持っています。誰かが助けることができますか?

+1

あなたが必要とするすべては、デバッグの目的のためにstderrに出力し、この情報であれば、あなたはExceptionDescribe()メソッドを使用することができます。 –

+0

これはC++では何もしないようです。私の場合、C++は実行可能ファイルであり、Javaは実行中のC++アプリケーションのコンテキストでロードされます。 –

+0

'exc.printStackTrace()'とまったく同じことをします。 – EJP

答えて

16

私は各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()を排除するように変更)からコピーしましたが、私が投稿したものはコンパイルしませんでしたが、一般的なアプローチはうまくいけば分かります。

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

をし、それをあなたのJavaコードの契約をしてみましょう:

+0

優秀、ありがとう。私は次の1日か2日にこれを構築し、これを答えとしてマークします。あまりにも悪いJNIは自動的にそれを世話する機能を提供しません - 弱点。 –

+1

@ DanNissenbaum、もっと良い方法があるかもしれませんが、それが私が見つけることができる唯一の方法です。これは非常に冗長です(継続的な 'ExceptionCheck()'のほうがずっと控えています)。 – hmjd

+0

あなたのコードは、一度変更することなくそのまま動作します。ありがとうございました。 (私は安全チェックなどを追加しています) –

-1

本のうち簡単な方法は、その後、すべての可能な例外をスローするJNIメソッドを宣言することです。

+0

これは、Javaコードが完全に終了した場合でも実行できますか?つまり、これは例外で終了する単一のJava関数を呼び出すC++実行可能ファイルですか? –

+0

@ダンニッセンバウムもちろんです。呼び出し元のJavaコードがない場合、例外を処理することはできません。 – EJP

+0

それで、私の質問の理由。 –

0

あなたができる例外のスタックトレースでちょうど興味がある場合:

if (env->ExceptionOccurred()) // check if an exception occurred 
{ 
    env->ExceptionDescribe(); // print the stack trace   
} 
関連する問題