2012-05-02 29 views
13

Tôi muốn xác định ngoại lệ nào đã được ném bởi một hàm Java khi gọi hàm đó từ mã C++ sử dụng JNI. Tôi có mã sau để bắt ngoại lệ Java:Làm thế nào để có được một mô tả về một ngoại lệ Java trong C++ khi sử dụng JNI?

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

Tôi không biết cách lấy thông tin mô tả về ngoại lệ Java trong mã C++ này. Ai đó có thể giúp?

+1

Nếu tất cả bạn cần là đầu ra thông tin này để thiết bị lỗi chuẩn cho mục đích gỡ lỗi, bạn có thể sử dụng ExceptionDescribe() phương pháp tiện lợi. –

+0

Điều đó dường như không làm gì trong C++. Trong trường hợp của tôi, C++ là tệp thực thi và Java được nạp trong ngữ cảnh của ứng dụng C++ đang chạy. –

+0

Nó thực hiện chính xác điều tương tự như 'exc.printStackTrace()'. – EJP

Trả lời

16

Tôi đã bỏ qua cuộc gọi ExceptionCheck() sau mỗi cuộc gọi JNI và kiểm tra mọi nỗ lực không thành công để định vị phương thức ngắn gọn: bạn nên thêm những điều này khi triển khai.

Đầu tiên, lưu trữ các ngoại lệ và sau đó mua lại các phương thức Java cần thiết cho việc thu thập thông tin về 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;"); 

Thứ hai, đệ quy xây dựng được thông báo lỗi (bạn có thể muốn thay đổi này):

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

Tôi đã sao chép mã này từ mã tôi đã viết vài năm trước (sửa đổi để loại bỏ tấm nồi hơi ExceptionCheck() s), nhưng tôi không biên dịch những gì tôi đã đăng nhưng cách tiếp cận chung là hy vọng rõ ràng.

+0

Tuyệt vời, cảm ơn. Tôi sẽ xây dựng điều này trong một hoặc hai ngày tiếp theo và sau đó đánh dấu phần này là câu trả lời. JNI quá xấu không cung cấp chức năng để tự động xử lý tất cả những điều đó - điểm yếu. –

+1

@DanNissenbaum, có thể có một cách tốt hơn nhưng đó là cách duy nhất tôi có thể tìm thấy. Nó khá dài dòng (thậm chí nhiều hơn như vậy với 'ExceptionCheck()' liên tục) để nói ít nhất. – hmjd

+0

Mã của bạn hoạt động như không có thay đổi duy nhất. Cảm ơn bạn. (Tôi đang thêm các kiểm tra an toàn, vv) –

-1

Các lối thoát dễ dàng của việc này là để khai báo các phương pháp JNI để ném tất cả các trường hợp ngoại lệ có thể, sau đó:

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

và để thỏa thuận đang Java của bạn với nó.

+0

Có thể thực hiện điều này ngay cả khi mã Java đã thoát hoàn toàn - tức là, đây là một tệp thực thi C++ có thể gọi một hàm Java đơn lẻ, có thoát khỏi ngoại lệ không? –

+0

@DanNissenbaum Tất nhiên là không. Nếu không có mã Java gọi thì nó không thể xử lý ngoại lệ. – EJP

+0

Do đó, lý do cho câu hỏi của tôi. –

0

Nếu bạn chỉ quan tâm đến các dấu vết ngăn xếp của ngoại lệ bạn có thể:

if (env->ExceptionOccurred()) // check if an exception occurred 
{ 
    env->ExceptionDescribe(); // print the stack trace   
} 
Các vấn đề liên quan