JNI Как вызвать java и перезвонить родным, и как получить JVM std io
Я разрабатываю в Windows, но не использую Windows lib.
JNI режима "инициативы", который сначала запускает java, а Systen.load()
затем вызывает собственный метод. Или «пассивный» режим. Исполняемый файл создает JVM, а JNI_CreateJavaVM
затем вызывает метод java.
Теперь я пытаюсь создать программу на C ++ с JNI SDK, поэтому эта вещь должна быть единственным исполняемым файлом, без DLL для 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, в этом нет никакого смысла.
Затем я пытаюсь использовать System.out/err
и try catch
найти issus, но получаю тот же результат. Эта вещь даже не может быть обнаружена с помощью исключения java или даже 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;
}
Работая нормально, я получил значение 1234 в консоли C ++.
※ Первый вопрос:
Как получить поток stdio JVM? System.out/err.print
не будет отображаться в консоли "инициативы". Но DLL std print будет печататься в 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 или что-то еще, я просто пишу тело функции в 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 не может ее найти.
Теперь ситуация такая:
Моя программа main
на C ++ - это запись, и я пишу java SDK для разработчика плагинов.
Во время выполнения C ++ создает JVM, загружает java-класс, вызывает java-метод при событии, и плагин использует native, чтобы «что-то делать». Итак, как это сделать?
И я тоже пробую
public static int sum(int a, int b) {
return a + b;
}
У меня 2, что нормально работает. Единственная проблема - это собственный вызов java.
Ответы
Чтобы получить доступ к собственным методам, вы все равно должны вызвать 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
экспортировал их в ваш двоичный файл.
Что касается вашего первого вопроса, нет отдельного потока stdio JVM, Java читает из того же fd=0
и записывает в то же, fd=1
что и все остальные.