JNI วิธีเรียก java และโทรกลับเนทีฟและวิธีรับ JVM std io
ฉัน dev ใน windows แต่ไม่ได้ใช้ windows lib
โหมด "ความคิดริเริ่ม" JNI ซึ่งเรียกใช้ java ก่อนSysten.load()
แล้วใช้เรียกวิธีเนทีฟ หรือโหมด "passive" ตัวเรียกใช้งานสร้าง 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
พบว่าเมืองอิสซัส แต่ได้ผลเหมือนกันว่าสิ่งที่แม้จะไม่สามารถจับโดยยกเว้น Java หรือแม้กระทั่ง try catch (...)
C
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 ++
※คำถามแรก:
วิธีรับสตรีม JVM stdio System.out/err.print
จะไม่แสดงในคอนโซล "ความคิดริเริ่ม" แต่การพิมพ์ DLL std จะพิมพ์ในคอนโซล java เมื่อโหมด "passive"
※คำถามที่สอง:
เกิดอะไรขึ้นในการเรียกพื้นเมือง? ฉันไม่ควรได้รับ 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:
เพื่อให้แน่ใจว่าจะไม่ออกจาก 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" {}
บล็อกทำงานทั้งหมดล้มเหลวเป็นผล UnsatisfiedLinkError
0
ดังนั้นฉันคิดว่าแม้แต่ฟังก์ชันที่จำเป็นดั้งเดิมในไฟล์ exe แต่ jvm ก็หาไม่เจอ
ตอนนี้สถานการณ์คือ:
โปรแกรม C ++ ของฉันคือmain
รายการและเขียน java SDK สำหรับนักพัฒนาปลั๊กอิน
ในรันไทม์ C ++ สร้าง JVM, Load java class, Invoke java method when event, and plugin use native to "do something" ดังนั้นจะทำอย่างไร?
และฉันก็ลองดู
public static int sum(int a, int b) {
return a + b;
}
ฉันได้ 2 ซึ่งใช้งานได้ดี ปัญหาเดียวคือ java call native
คำตอบ
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
ข้ออื่น ๆ ทั้งหมด