JNI 자바를 호출하고 네이티브를 콜백하는 방법 및 JVM std io를 얻는 방법

Aug 18 2020

나는 Windows에서 dev이지만 Windows lib를 사용하지 않습니다.

Java를 먼저 실행하고 Systen.load()네이티브 메서드를 호출 하는 "초기"모드 JNI입니다 . 또는 "수동"모드, 실행 파일은 JVM JNI_CreateJavaVM을 생성 한 다음 java 메소드를 호출합니다.

이제 JNI SDK를 사용하여 C ++ 프로그램을 만들려고합니다. 따라서 해당 항목은 단일 실행 파일이어야하며 System.load().

먼저 hello world를 작성하십시오.

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, 그것은 절대적으로 말이되지 않습니다.

그리고 내가 사용하려고 System.out/err하고 try catchissus을 찾을 같은 결과를 얻을, 그 것은 심지어 자바 예외 또는 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 print는 "passive"모드 일 때 Java 콘솔에서 인쇄됩니다.

※ 두 번째 질문 :

기본 통화에서는 어떻게 되나요? 0 결과가 나오면 안되는데 어떻게 고쳐야하나요? 목표를 달성하는 방법?

BYW-의미가 없지만 좋은 시도 : 사용 CallObjectMethod하면 동일한 결과가 표시되고 사용 GetMethodID하면 ID 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 :

extern C 또는 thar가 아닌지 확인하기 위해 함수 본문을 h에 작성합니다.

#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항목이며 플러그인 개발자 용 Java SDK를 작성합니다.

런타임에서 C ++는 JVM을 생성하고, 자바 클래스를로드하고, 이벤트가 발생하면 자바 메서드를 호출하고, 플러그인은 네이티브를 사용하여 "무언가"를 수행합니다.

그리고 나는 또한 시도

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 스트림이 없으며 Java는 동일에서 읽고 다른 모든 fd=0것과 동일 fd=1하게 씁니다 .