将 jstring 转换为 c++ 字符串导致 java 代码执行出错
Converting jstring to c++ string results to error in java code execution
我正在通过 jni 将一个 java 对象传递给 c++,其中有一个字符串作为属性。
我尝试将其转换为字符串,以便将该变量传递给 C++ 构造函数。
Java Class
import at.xxxx.calculatorhuman.HumanBean;
public class Controller {
public static void main(String[] args) {
System.out.println("library: "
+ System.getProperty("java.library.path"));
CalculatorController calcController = new CalculatorController();
HumanController humanController = new HumanController();
HumanBean human = new HumanBean("John Doe", 16, 3000, 2988.77);
int ageOfHuman = humanController.getAgeOfHuman(human);
}
}
HumanBean.java
package at.xxxx.calculatorhuman;
public class HumanBean {
String name;
int alter;
int gehalt;
double gehaltDouble;
public HumanBean(String name, int alter, int gehalt, double gehaltDouble) {
super();
this.name = name;
this.alter = alter;
this.gehalt = gehalt;
this.gehaltDouble = gehaltDouble;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAlter() {
return alter;
}
public void setAlter(int alter) {
this.alter = alter;
}
public int getGehalt() {
return gehalt;
}
public void setGehalt(int gehalt) {
this.gehalt = gehalt;
}
public double getGehaltDouble() {
return gehaltDouble;
}
public void setGehaltDouble(double gehaltDouble) {
this.gehaltDouble = gehaltDouble;
}
@Override
public String toString() {
return "HumanBean [name=" + name + ", alter=" + alter + ", gehalt=" + gehalt + ", gehaltDouble=" + gehaltDouble
+ "]";
}
}
C++ 文件
/*
* Class: HumanController
* Method: getAgeOfHuman
* Signature: (LHumanBean;)I
*/
JNIEXPORT jint JNICALL Java_HumanController_getAgeOfHuman(JNIEnv *env, jclass obj, jobject objarg) {
std::cout << "-------------------------Java_HumanController_getAgeOfHuman---------------------------" << endl;
jclass cls = (*env).GetObjectClass(objarg);
/////////////////////////////////
jfieldID fidName = (*env).GetFieldID(cls, "name", "Ljava/lang/String;");
std::cout << "First Line Finished - jfieldID = " << fidName << endl;
jstring nameStringJNI = (jstring)(*env).GetObjectField(cls, fidName);
std::cout << "Second Line Finished - jstring = " << nameStringJNI << endl;
const char* raw = env->GetStringUTFChars(nameStringJNI, NULL);
std::cout << "Third Line Finished - raw = " << raw << endl;
std::string str = std::string(raw);
std::cout << "Fourth Line Finished - str = " << str << endl;
std::cout << "Java_HumanController_getAgeOfHuman c++ Name " << str << endl;
/////////////////////////////////
Human humanMirror = createHuman(str, age, gehaltInt, gehaltDouble);
return humanMirror.alter;
}
如您所见,它只编译前两行 - 之后它抛出错误消息。 (它抛出 const char* raw = env->GetStringUTFChars(nameStringJNI, NULL);
)
我已经尝试了很多不同的解决方案来将 jstring 转换为字符串,因为我使用的构造函数不接受 jstring 作为参数。我尝试的每个解决方案都会导致相同的错误。
导致此错误的原因是什么以及如何解决?
在我的 jni.h
(Fedora 29) 中,jstring
被定义为指向 _jstring
结构的指针。
在您的结果中,nameStringJNI
打印为 000000 = NULL 指针。
您的 C++ 代码实现不正确,尤其是您对 (*env).GetObjectField()
的调用是错误的。 HumanBean.name
字段不是 static
字段,因此您需要向它传递一个指向 HumanBean
对象实例 的指针(位于 objarg
参数)作为从中读取 name
字段的对象。但是你传递给它一个指向 HumanBean
class 类型 的指针(从你从 objarg
中提取的 cls
变量) ,导致 GetObjectField()
到 return 您未处理的 NULL 指针。
试试这个:
/*
* Class: HumanController
* Method: getAgeOfHuman
* Signature: (LHumanBean;)I
*/
JNIEXPORT jint JNICALL Java_HumanController_getAgeOfHuman(JNIEnv *env, jclass obj, jobject objarg)
{
std::cout << "-------------------------Java_HumanController_getAgeOfHuman---------------------------" << std::endl;
if (!objarg) {
std::cout << "objarg is null!" << std::endl;
return -1; // or env->Throw() an exception...
}
jclass cls = env->GetObjectClass(objarg);
// TODO: verify that objarg is really an instance of the
// "at.xxxx.calculatorhuman.HumanBean" class before doing
// anything with it...
/////////////////////////////////
jfieldID fidName = env->GetFieldID(cls, "name", "Ljava/lang/String;");
std::cout << "First Line Finished - jfieldID = " << fidName << std::endl;
if (!fidName) {
std::cout << "Could not find objarg.name field!" << std::endl;
return -1; // or env->Throw() an exception...
}
jstring nameStringJNI = (jstring) env->GetObjectField(objarg, fidName); // <-- use objarg, not cls!
std::cout << "Second Line Finished - jstring = " << nameStringJNI << std::endl;
if (!nameStringJNI) {
std::cout << "Could not get pointer to objarg.name field!" << std::endl;
return -1; // or env->Throw() an exception...
}
// NOTE: Java uses *modified* UTF-8! Consider using env->GetStringChars()
// with std::wstring instead...
const char* raw = env->GetStringUTFChars(nameStringJNI, NULL);
std::cout << "Third Line Finished - raw = " << (const void*)raw << std::endl;
if (!raw) {
std::cout << "Could not get pointer to objarg.name content!" << std::endl;
return -1; // or env->Throw() an exception...
}
std::string str(raw); // <-- can't construct std::string with a NULL pointer!
env->ReleaseStringUTFChars(nameStringJNI, raw); // <-- avoid a memory leak!
std::cout << "Fourth Line Finished - str = " << str << endl;
std::cout << "Java_HumanController_getAgeOfHuman c++ Name " << str << endl;
/////////////////////////////////
Human humanMirror = createHuman(str, age, gehaltInt, gehaltDouble);
return humanMirror.alter;
}
我正在通过 jni 将一个 java 对象传递给 c++,其中有一个字符串作为属性。 我尝试将其转换为字符串,以便将该变量传递给 C++ 构造函数。
Java Class
import at.xxxx.calculatorhuman.HumanBean;
public class Controller {
public static void main(String[] args) {
System.out.println("library: "
+ System.getProperty("java.library.path"));
CalculatorController calcController = new CalculatorController();
HumanController humanController = new HumanController();
HumanBean human = new HumanBean("John Doe", 16, 3000, 2988.77);
int ageOfHuman = humanController.getAgeOfHuman(human);
}
}
HumanBean.java
package at.xxxx.calculatorhuman;
public class HumanBean {
String name;
int alter;
int gehalt;
double gehaltDouble;
public HumanBean(String name, int alter, int gehalt, double gehaltDouble) {
super();
this.name = name;
this.alter = alter;
this.gehalt = gehalt;
this.gehaltDouble = gehaltDouble;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAlter() {
return alter;
}
public void setAlter(int alter) {
this.alter = alter;
}
public int getGehalt() {
return gehalt;
}
public void setGehalt(int gehalt) {
this.gehalt = gehalt;
}
public double getGehaltDouble() {
return gehaltDouble;
}
public void setGehaltDouble(double gehaltDouble) {
this.gehaltDouble = gehaltDouble;
}
@Override
public String toString() {
return "HumanBean [name=" + name + ", alter=" + alter + ", gehalt=" + gehalt + ", gehaltDouble=" + gehaltDouble
+ "]";
}
}
C++ 文件
/*
* Class: HumanController
* Method: getAgeOfHuman
* Signature: (LHumanBean;)I
*/
JNIEXPORT jint JNICALL Java_HumanController_getAgeOfHuman(JNIEnv *env, jclass obj, jobject objarg) {
std::cout << "-------------------------Java_HumanController_getAgeOfHuman---------------------------" << endl;
jclass cls = (*env).GetObjectClass(objarg);
/////////////////////////////////
jfieldID fidName = (*env).GetFieldID(cls, "name", "Ljava/lang/String;");
std::cout << "First Line Finished - jfieldID = " << fidName << endl;
jstring nameStringJNI = (jstring)(*env).GetObjectField(cls, fidName);
std::cout << "Second Line Finished - jstring = " << nameStringJNI << endl;
const char* raw = env->GetStringUTFChars(nameStringJNI, NULL);
std::cout << "Third Line Finished - raw = " << raw << endl;
std::string str = std::string(raw);
std::cout << "Fourth Line Finished - str = " << str << endl;
std::cout << "Java_HumanController_getAgeOfHuman c++ Name " << str << endl;
/////////////////////////////////
Human humanMirror = createHuman(str, age, gehaltInt, gehaltDouble);
return humanMirror.alter;
}
如您所见,它只编译前两行 - 之后它抛出错误消息。 (它抛出 const char* raw = env->GetStringUTFChars(nameStringJNI, NULL);
)
我已经尝试了很多不同的解决方案来将 jstring 转换为字符串,因为我使用的构造函数不接受 jstring 作为参数。我尝试的每个解决方案都会导致相同的错误。
导致此错误的原因是什么以及如何解决?
在我的 jni.h
(Fedora 29) 中,jstring
被定义为指向 _jstring
结构的指针。
在您的结果中,nameStringJNI
打印为 000000 = NULL 指针。
您的 C++ 代码实现不正确,尤其是您对 (*env).GetObjectField()
的调用是错误的。 HumanBean.name
字段不是 static
字段,因此您需要向它传递一个指向 HumanBean
对象实例 的指针(位于 objarg
参数)作为从中读取 name
字段的对象。但是你传递给它一个指向 HumanBean
class 类型 的指针(从你从 objarg
中提取的 cls
变量) ,导致 GetObjectField()
到 return 您未处理的 NULL 指针。
试试这个:
/*
* Class: HumanController
* Method: getAgeOfHuman
* Signature: (LHumanBean;)I
*/
JNIEXPORT jint JNICALL Java_HumanController_getAgeOfHuman(JNIEnv *env, jclass obj, jobject objarg)
{
std::cout << "-------------------------Java_HumanController_getAgeOfHuman---------------------------" << std::endl;
if (!objarg) {
std::cout << "objarg is null!" << std::endl;
return -1; // or env->Throw() an exception...
}
jclass cls = env->GetObjectClass(objarg);
// TODO: verify that objarg is really an instance of the
// "at.xxxx.calculatorhuman.HumanBean" class before doing
// anything with it...
/////////////////////////////////
jfieldID fidName = env->GetFieldID(cls, "name", "Ljava/lang/String;");
std::cout << "First Line Finished - jfieldID = " << fidName << std::endl;
if (!fidName) {
std::cout << "Could not find objarg.name field!" << std::endl;
return -1; // or env->Throw() an exception...
}
jstring nameStringJNI = (jstring) env->GetObjectField(objarg, fidName); // <-- use objarg, not cls!
std::cout << "Second Line Finished - jstring = " << nameStringJNI << std::endl;
if (!nameStringJNI) {
std::cout << "Could not get pointer to objarg.name field!" << std::endl;
return -1; // or env->Throw() an exception...
}
// NOTE: Java uses *modified* UTF-8! Consider using env->GetStringChars()
// with std::wstring instead...
const char* raw = env->GetStringUTFChars(nameStringJNI, NULL);
std::cout << "Third Line Finished - raw = " << (const void*)raw << std::endl;
if (!raw) {
std::cout << "Could not get pointer to objarg.name content!" << std::endl;
return -1; // or env->Throw() an exception...
}
std::string str(raw); // <-- can't construct std::string with a NULL pointer!
env->ReleaseStringUTFChars(nameStringJNI, raw); // <-- avoid a memory leak!
std::cout << "Fourth Line Finished - str = " << str << endl;
std::cout << "Java_HumanController_getAgeOfHuman c++ Name " << str << endl;
/////////////////////////////////
Human humanMirror = createHuman(str, age, gehaltInt, gehaltDouble);
return humanMirror.alter;
}