如何在JNI中访问jobject的值

3
JNIEXPORT jbyteArray JNICALL Java_ramRead_MainClass_readRam
        (JNIEnv *env, jobject, jobject len, jobject addr, jint pid)
{
        jclass c = (*env).GetObjectClass(len);
        jfieldID fid = (*env).GetFieldID(c, "len", "Ljava/math/BigInteger;");
        std::cout << "Object Field: " << (*env).GetObjectField(len, fid);

        struct iovec local[1];
        struct iovec remote[1];
        unsigned long long len1 = (*env).GetObjectField(len, fid) //This did not work out. the conversion to unsigned long long
        jbyte buf[len];
        jbyteArray buf1 = (*env).NewByteArray(len);

        local[0].iov_base = buf;
        local[0].iov_len = len;

        remote[0].iov_base = (void *) addr;
        remote[0].iov_len = len;

        nread = process_vm_readv(pid, local, 1, remote, 1, 0);

        (*env).SetByteArrayRegion(buf1, 0, len, buf);
        return buf1;
}

这是我的本地方法,正如您所看到的,它接收3个参数作为参数,其中两个是jobject和一个是int。这两个jobjects是大整数,最初它作为参数接收了两个int和一个long,但是由于现在我需要传递大整数,所以我有一个问题,或者应该说有几个问题。
目标:
我需要从jobject len获取BigInteger值,然后使用它来初始化jbyte buf[len]jbyteArray buf1=(*env).NewByteArray(len)local[0].iov_len=len;的大小。当然,我不能只用len来初始化它们,我需要从len中获取大整数值,然后用它来初始化大小。 然后我需要做同样的事情,从jobject addr中提取大整数值。
任何帮助都将不胜感激。

如果您最终将其转换为无符号长整型,则我不明白在此处使用 BigInteger 的意义何在。为什么不直接传递 Java 的 long 呢? - Jorn Vernee
我尝试将其转换为long long,但没有成功。为什么要使用BigInteger?嗯,这个数字非常大,举个例子,这是十六进制数中的一个数字:ffffffffff601000。 - ChronicUser
1个回答

4
您的方法存在问题,因为您正在尝试访问BigInteger调用len的字段:
GetFieldID(c, "len", "Ljava/math/BigInteger;");

然而BigInteger没有这样的字段。

没有办法直接访问BigInteger的"value"。在JNI中,你可以通过调用Java方法longValue(在BigInteger上),将BigInteger转换为jlong并使用它。

但是,如果你将BigInteger转换为jlong,那么使用BigInteger就没有任何意义了。你会失去使用BigInteger获得的任何精度。因此,最好在Java中使用long(该值仍适用于您提到的0xffffffffff601000)。

此外,byte[]只能使用int进行索引,所以即使你使buf比那更大,它也无法从Java中访问。

我建议使用一个直接的ByteBuffer而不是一个byte[],因为你不需要跨缓冲区复制数据。

你可以得到以下结果:

private static native void readRam(ByteBuffer buff, long address, int pid);

然后在C++中:

JNIEXPORT void JNICALL Java_Main_readRam
    (JNIEnv *env, jclass, jobject byteBuffer, jlong addr jint pid) {

    struct iovec local[1];
    struct iovec remote[1];

    void* buf = env->GetDirectBufferAddress(byteBuffer);
    jlong len = env->GetDirectBufferCapacity(byteBuffer);

    local[0].iov_base = buf;
    local[0].iov_len = len;

    remote[0].iov_base = (void *) addr;
    remote[0].iov_len = len;

    process_vm_readv(pid, local, 1, remote, 1, 0);
}

然后调用:

int bufferLength = ...;
long address = 0xffffffffff601000L;
int pid = ...;

ByteBuffer bb = ByteBuffer.allocateDirect(bufferLength);

readRam(bb, address, pid);

// use bb...

关于您的评论,您可以这样做:
StringBuilder sb = new StringBuilder();
while(bb.hasRemaining()) {
    byte b = bb.get();
    if((b >= 32 && b < 127) || b == 10) {
        sb.append((char) b);
    }
}
String result = sb.toString();

谢谢你的帮助,但我之前也尝试过使用字节数组,它可以工作,但我认为我需要大整数,然后就变得混乱了。现在的问题是当我尝试使用bb.hasRemaining()和bb.get()读取ByteBuffer时,我会得到BufferUnderflowException异常。我已经阅读了文档,但似乎无法解决它。此外,我需要将这个bytebuffer的元素解释为字符并将它们存储到一个字符串中。这就是我用byte[]数组所做的。 - ChronicUser
byte[] x = this.readRam(readSize, addr, PID); String s = ""; for(int i=0; i<x.length; i++) { if(((int)x[i] >= 32 && (int)x[i] < 127) || (int)x[i]==10) { s = s+(char)x[i]; } } return s; - ChronicUser
非常感谢,它像魔法一样工作,但我希望自己有更多的声望,这样我至少可以支持您的答案。 - ChronicUser

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接