JNI जावा को कैसे कॉल करें और मूल निवासी को कॉल करें, और JVM std io कैसे प्राप्त करें

Aug 18 2020

मैं खिड़कियों में देव हूँ, लेकिन खिड़कियों का उपयोग नहीं कर रहा हूँ।

"पहल" मोड जेएनआई जो पहले जावा चलाते हैं और Systen.load()फिर मूल विधि का उपयोग करते हैं । या "निष्क्रिय" मोड, निष्पादन योग्य जेवीएम बनाएं JNI_CreateJavaVMफिर जावा विधि को कॉल करें।

अब, मैं जेएनआई एसडीके के साथ एक सी ++ प्रोग्राम बनाने की कोशिश कर रहा हूं, इसलिए उस चीज को एकल निष्पादन योग्य होना चाहिए, इसके लिए कोई डीएल नहीं System.load()

पहले एक नमस्ते दुनिया लिखें:

public static native int nativeSum(int a, int b);

public static int sum(int a, int b) {
    return nativeSum(a, b);
}

और रन javah Driverको यह हेडर डिफाइन मिला

JNIEXPORT jint JNICALL Java_Driver_nativeSum (JNIEnv *, jclass, jint, jint);

और javap -s Driverसही नाम का उपयोग करके सुनिश्चित करें

  public static native int nativeSum(int, int);
    descriptor: (II)I

  public static int sum(int, int);
    descriptor: (II)I

main.cpp लिखें

#include <iostream>
#include "jni.h"
#include "Driver.h"


JNIEXPORT jint JNICALL Java_Driver_nativeSum(JNIEnv*, jclass, jint a, jint b) {
    std::cout << "Native invoked " << std::endl;
    return a + b;
}


int main() {
    
    JavaVMInitArgs vm_args;
    
    vm_args.version = JNI_VERSION_1_8;
    vm_args.ignoreUnrecognized = true;
    vm_args.nOptions = 1;
    
    auto* options = new JavaVMOption[1];
    
    std::string cmd = "-Djava.class.path=../class/out/production/class";
    
    options[0].optionString = const_cast<char*>(cmd.c_str());
    
    vm_args.options = options;
    
    JavaVM* jvm;
    JNIEnv* env;
    
    jint rc = JNI_CreateJavaVM(&jvm, (void**) &env, &vm_args);

    delete[] options;


    // ==========================================================

    _jclass* jClass_Driver = env->FindClass("Driver");

    _jmethodID* jMethod_Driver_sum = env->GetStaticMethodID(
            jClass_Driver,
            "sum",
            "(II)I"
    );

    std::cout << "Test-sum method id = " << jMethod_Driver_sum << std::endl;

    long jResult_Driver_sum = env->CallStaticIntMethod(
            jClass_Driver,
            jMethod_Driver_sum,
            1, 1
    );

    std::cout << "Test-sum Method called res - "
              << jResult_Driver_sum
              << std::endl;


    // ==========================================================



    jvm->DestroyJavaVM();


    return 0;

}

परिणाम:

VM created
Test-sum method id = 0x1ebf4888
Test-sum Method called res - 0

Process finished with exit code 0

खैर, 1 + 1 = 0, यह बिल्कुल कोई मतलब नहीं है।

तब मैं issus का उपयोग करने System.out/errऔर try catchखोजने की कोशिश करता हूं , लेकिन एक ही परिणाम प्राप्त करता है, वह चीज जावा अपवाद या यहां तक ​​कि C ++ द्वारा भी पकड़ नहीं सकती है try catch (...)

public static int sum(int a, int b) {
    try {
        return nativeSum(a, b);
    } catch (Exception exception) {
        return -1;
    }
}

तो सुनिश्चित करें कि किसी भी गलती नहीं है, मैं मूल निवासी बाईपास:

public static int sum(int a, int b) {
    return 1234;
}

बहुत अच्छा काम करते हुए, मुझे C ++ कंसोल में 1234 मूल्य मिला।

※ पहला सवाल:

JVM stdio धारा कैसे प्राप्त करें? System.out/err.print"पहल" कंसोल में शो नहीं होगा। लेकिन "निष्क्रिय" मोड होने पर DLL std प्रिंट जावा कंसोल में प्रिंट होगा।

※ दूसरा सवाल:

देशी कॉल में क्या होता है? मुझे 0 परिणाम नहीं मिलना चाहिए, इसे कैसे ठीक करें? लक्ष्य कैसे प्राप्त करें?

BYW - कोई मतलब नहीं है, लेकिन अच्छा प्रयास करें: उपयोग CallObjectMethodकरने से एक ही परिणाम मिलेगा, GetMethodIDID 0 का उपयोग करके वापस आ जाएगा और 1073741819 (0xC0000005) के साथ एक लंबा अटक गया।

अपडेट 1:

jmethodID jMethod_Driver_nativeSum = env->GetStaticMethodID(
    jClass_Driver,
    "nativeSum",
    "(II)I"
);

std::cout << jMethod_Driver_nativeSum << std::endl;

jint jResult_Driver_nativeSum = env->CallStaticIntMethod(
    jClass_Driver,
    jMethod_Driver_sum,
    1, 1
);

std::cout << jResult_Driver_nativeSum << std::endl;

यह आउटपुट मिला

method id = 0x1ec97350
method result = 0

अपडेट 2:

यह सुनिश्चित करने के लिए कि सी या थार को बाहर न करें, मैं सिर्फ एच में फ़ंक्शन बॉडी लिखता हूं

#include <jni.h>

/*
 * Class:     Driver
 * Method:    nativeSum
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_Driver_nativeSum(JNIEnv* env, jclass cls, jint a, jint b) {
    std::cout << "Java_Driver_nativeSum invoked" << std::endl;
    return a + b;
}

और फ़ंक्शन नाम सही है यह सुनिश्चित करने के लिए उपकरण का उपयोग करें

और env->ExceptionOccurredअपवाद प्राप्त करने के लिए उपयोग करें, और एक है:

java.lang.UnsatisfiedLinkError: Driver.nativeSum(II)I

और exter "C" {}ब्लॉक के साथ या बिना कोई भी काम नहीं कर रहा है, सभी परिणाम 0 के रूप में विफल रहे और UnsatisfiedLinkError

तो, मुझे लगता है कि exe फ़ाइल में भी मूल आवश्यक फ़ंक्शन, jvm इसे नहीं ढूंढ सकता है।

अब स्थिति यह है:

मेरी C ++ प्रोग्राम mainप्रविष्टि है, और प्लगइन डेवलपर के लिए जावा एसडीके लिखें।

रनटाइम में, C ++ JVM, Load java class, Invoke java मेथड बनाते हैं जब ईवेंट, और प्लगइन "कुछ करने" के लिए देशी का उपयोग करते हैं, तो कैसे करें?

और मैं भी कोशिश करता हूं

public static int sum(int a, int b) {
    return a + b;
}

मुझे 2 मिला, जो ठीक काम कर रहा है। केवल समस्या है जावा कॉल देशी।

जवाब

AlexCohn Aug 19 2020 at 14:16

देशी तरीकों का उपयोग करने के लिए, आपको अभी भी कॉल करना होगा System.LoadLibrary()। कल्पना बताते हैं कि अपने Driver.java शामिल दिखना चाहिए:

public class Driver {
  static { System.loadLibrary("driver"); } // this name must be matched!
  public static native int nativeSum(int a, int b);

  public static int sum(int a, int b) {
    return nativeSum(a, b);
  }
}

और अपने main.cpp में ,

extern "C" JNIEXPORT jint JNICALL Java_Driver_nativeSum(JNIEnv*, jclass, jint a, jint b) {
  std::cout << "Native invoked " << std::endl;
  return a + b;
}

extern "C" JNIEXPORT jint JNI_OnLoad_driver // this suffix must match the name used in Java
                                           (JavaVM *vm, void *reserved) {
  std::cout << "Native loaded" << std::endl;
  return JNI_VERSION_1_8;
}

सुनिश्चित करें कि लिंकर दोनों को रखता है Java_Driver_nativeSumऔर JNI_OnLoad_driverआपके बाइनरी में निर्यात किया जाता है।

आपके पहले प्रश्न के लिए, कोई अलग JVM stdio स्ट्रीम नहीं है, जावा एक ही से पढ़ता है fd=0और fd=1सभी अन्य लोगों के समान लिखता है ।