将 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;
}