英文:
JNI How to call java and call back native, And how to get JVM std io
问题
#include <iostream>
#include "jni.h"
#include "Driver.h"
JNIEXPORT jint JNICALL Java_Driver_nativeSum(JNIEnv* env, 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;
}
Update 1:
jmethodID jMethod_Driver_nativeSum = env->GetStaticMethodID(
jClass_Driver,
"nativeSum",
"(II)I"
);
std::cout << "method id = " << jMethod_Driver_nativeSum << std::endl;
jint jResult_Driver_nativeSum = env->CallStaticIntMethod(
jClass_Driver,
jMethod_Driver_sum,
1, 1
);
std::cout << "method result = " << jResult_Driver_nativeSum << std::endl;
Update 2:
#include <jni.h>
#include <iostream>
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;
}
And use env->ExceptionOccurred
to get Exception, And there is one:
java.lang.UnsatisfiedLinkError: Driver.nativeSum(II)I
And None of with or without extern "C" {}
block is working, All failed as result 0 and UnsatisfiedLinkError
.
So, I think even the native required function in the exe file, The jvm can't find it.
Now the situation is:
My C++ program is main
the entry, And write java SDK for plugin developer.
In runtime, C++ create JVM, Load java class, Invoke java method when event, And plugin use native to "do something", So how to ?
And I also try:
public static int sum(int a, int b) {
return a + b;
}
I got 2, Which is working fine. Only problem is java call native.
<details>
<summary>英文:</summary>
I'm dev in windows but not using windows lib.
The "initiative" mode JNI which run java first and using `Systen.load()` then call native method.
Or "passive" mode, The executable create JVM `JNI_CreateJavaVM` then call java method.
Now, I'm trying make a C++ program with JNI SDK, So that thing must be single executable, No dll for `System.load()`.
First write a hello world:
```java
public static native int nativeSum(int a, int b);
public static int sum(int a, int b) {
return nativeSum(a, b);
}
and run javah Driver
got this header define
JNIEXPORT jint JNICALL Java_Driver_nativeSum (JNIEnv *, jclass, jint, jint);
and run javap -s Driver
make sure using right name
public static native int nativeSum(int, int);
descriptor: (II)I
public static int sum(int, int);
descriptor: (II)I
write the 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;
}
Result:
VM created
Test-sum method id = 0x1ebf4888
Test-sum Method called res - 0
Process finished with exit code 0
Well, 1 + 1 = 0, That absolutely make none sense.
Then I try to using System.out/err
and try catch
find the issus but get same result, That thing even cannot catch by java exception or even C++ try catch (...)
.
public static int sum(int a, int b) {
try {
return nativeSum(a, b);
} catch (Exception exception) {
return -1;
}
}
Then make sure not anyother mistake, I bypass native:
public static int sum(int a, int b) {
return 1234;
}
Working pretty fine, I got the 1234 value in C++ console.
※ First Question:
How to get JVM stdio stream? System.out/err.print
wont show in "initiative" console.
But DLL std print will print in java console when "passive" mode.
※ Second question:
What happen in the native call? I should not get 0 result, How to fix it?
How to achieve the goal?
BYW - make no sense but nice try : using CallObjectMethod
will get same result, using GetMethodID
will return ID 0 and a long stuck exit with 1073741819 (0xC0000005).
Update 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;
Got this output
method id = 0x1ec97350
method result = 0
Update 2:
To make sure not extern C or thar else I just write the function body in 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;
}
And use tool to make sure the function name is correct
And use env->ExceptionOccurred
to get Exception, And there is one:
java.lang.UnsatisfiedLinkError: Driver.nativeSum(II)I
And None of with or without exter "C" {}
block is working, All failed as result 0 and UnsatisfiedLinkError
.
So, I think even the native required function in the exe file, The jvm can't find it.
Now the situation is :
My C++ program is main
the entry, And write java SDK for plugin developer.
In runtime, C++ create JVM, Load java class, Invoke java method when event, And plugin use native to "do something", So how to ?
And I also try
public static int sum(int a, int b) {
return a + b;
}
I got 2 , Which is working fine. Only problem is java call native.
答案1
得分: 1
要访问本地方法,仍然必须调用 System.LoadLibrary()
。规范 解释了你的 Driver.java 应该包含以下内容:
public class Driver {
static { System.loadLibrary("driver"); } // 必须与此名称匹配!
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 // 此后缀必须与 Java 中使用的名称匹配
(JavaVM *vm, void *reserved) {
std::cout << "Native loaded" << std::endl;
return JNI_VERSION_1_8;
}
确保链接器在你的二进制文件中导出 Java_Driver_nativeSum
和 JNI_OnLoad_driver
两者。
至于你的第一个问题,没有单独的 JVM 标准输入/输出流,Java 从与其他所有内容相同的 fd=0
读取,并写入相同的 fd=1
。
英文:
To access native methods, you still must call System.LoadLibrary()
. The spec explains that your Driver.java should look contain:
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);
}
}
and in your 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;
}
Make sure that the linker keeps both Java_Driver_nativeSum
and JNI_OnLoad_driver
exported in your binary.
As for your first question, there is no separate JVM stdio stream, Java reads from the same fd=0
and writes to same fd=1
as all others.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论