JNI Cómo llamar a Java y devolver la llamada nativa, y cómo obtener JVM std io
Soy desarrollador en Windows pero no uso Windows lib.
El modo "iniciativo" JNI que ejecuta Java primero y Systen.load()
luego llama al método nativo. O modo "pasivo", el ejecutable crea JVM JNI_CreateJavaVM
y luego llama al método java.
Ahora, estoy tratando de hacer un programa C ++ con JNI SDK, por lo que debe ser un solo ejecutable, sin dll para System.load()
.
Primero escribe un hola mundo:
public static native int nativeSum(int a, int b);
public static int sum(int a, int b) {
return nativeSum(a, b);
}
y ejecutar javah Driver
tiene este encabezado definido
JNIEXPORT jint JNICALL Java_Driver_nativeSum (JNIEnv *, jclass, jint, jint);
y ejecute javap -s Driver
asegúrese de usar el nombre correcto
public static native int nativeSum(int, int);
descriptor: (II)I
public static int sum(int, int);
descriptor: (II)I
escribir el 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;
}
Resultado:
VM created
Test-sum method id = 0x1ebf4888
Test-sum Method called res - 0
Process finished with exit code 0
Bueno, 1 + 1 = 0, eso no tiene absolutamente ningún sentido.
Luego trato de usar System.out/err
y try catch
encontrar el problema pero obtengo el mismo resultado, esa cosa ni siquiera puede detectarse por excepción de Java o incluso C++ try catch (...)
.
public static int sum(int a, int b) {
try {
return nativeSum(a, b);
} catch (Exception exception) {
return -1;
}
}
Entonces asegúrese de que no haya ningún otro error, omito nativo:
public static int sum(int a, int b) {
return 1234;
}
Trabajando bastante bien, obtuve el valor 1234 en la consola C++.
※ Primera pregunta:
¿Cómo obtener la transmisión JVM stdio? System.out/err.print
no se mostrará en la consola de "iniciativa". Pero la impresión estándar de DLL se imprimirá en la consola Java cuando esté en modo "pasivo".
※ Segunda pregunta:
¿Qué sucede en la llamada nativa? No debería obtener 0 resultado, ¿Cómo solucionarlo? ¿Cómo lograr el objetivo?
BYW: no tiene sentido, pero buen intento: el uso CallObjectMethod
obtendrá el mismo resultado, el uso GetMethodID
devolverá la ID 0 y una salida larga atascada con 1073741819 (0xC0000005).
Actualización 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;
Tengo esta salida
method id = 0x1ec97350
method result = 0
Actualización 2:
Para asegurarme de que no sea C externo o de lo contrario, solo escribo el cuerpo de la función en 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;
}
Y use la herramienta para asegurarse de que el nombre de la función sea correcto

Y use env->ExceptionOccurred
para obtener una excepción, y hay una:
java.lang.UnsatisfiedLinkError: Driver.nativeSum(II)I
Y ninguno con o sin exter "C" {}
bloque funciona, todos fallaron como resultado 0 y UnsatisfiedLinkError
.
Entonces, creo que incluso la función nativa requerida en el archivo exe, el jvm no puede encontrarla.
Ahora la situación es:
Mi programa C ++ es main
la entrada, y escribo Java SDK para el desarrollador de complementos.
En tiempo de ejecución, C ++ crea JVM, carga la clase java, invoca el método java cuando ocurre un evento y el complemento usa nativo para "hacer algo", entonces, ¿cómo?
y tambien intento
public static int sum(int a, int b) {
return a + b;
}
Tengo 2, que está funcionando bien. El único problema es la llamada nativa de Java.
Respuestas
Para acceder a los métodos nativos, aún debe llamar a System.LoadLibrary()
. La especificación explica que su Driver.java debería contener:
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);
}
}
y en su 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;
}
Asegúrese de que el enlazador mantenga ambos Java_Driver_nativeSum
y los JNI_OnLoad_driver
exporte en su binario.
En cuanto a su primera pregunta, no hay una secuencia JVM stdio separada, Java lee de la misma fd=0
y escribe igual fd=1
que todos los demás.