Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing a Java object in a Java object in C using JNI

I'm relatively new to JNI and have gotten down the basics of messing with integers and arrays in Java objects using JNI. Now I'm trying to modify/access a Java object within a Java object.

I've been searching on the internet and on Stack Overflow and have yet to find out how to do this.

Here's the example.

In Java:

public class ObjectOne
{
    private byte[] buff;
    ...
    ...
}

public class ObjectTwo
{
    private ObjectOne obj;
    ...
    ...
}

In JNI, how do I access "buff" from ObjectOne through ObjectTwo? I tried something like this...

JNIEXPORT void JNICALL Java_accessBuffThroughObjectTwo(JNIEnv *env, jobject obj, jobject objectTwo)
{
    jclass clazz;
    jclass bufferClazz;
    jobject bufferJObject;

    clazz = (*env)->GetObjectClass(env, objectTwo);
    fid = (*env)->GetFieldID(env, clazz, "obj", "Ljava/lang/Object;");
    bufferJObject = (*env)->GetObjectField(env, javascsicommand, fid);
    bufferClazz = (*env)->GetObjectClass(env, bufferJObject);  <-- Fails here for Access Violation
    fid = (*env)->GetFieldID(env, bufferClazz, "buff", "[B");
}

Any help on what I'm doing wrong?

like image 320
user1575243 Avatar asked Aug 03 '12 22:08

user1575243


People also ask

What is Jclass in JNI?

The jclass instance is your object on which a method will be invoked; you'll need to look up the getName method ID on the Class class, then invoke it on the jclass instance using CallObjectMethod to obtain a jstring result. So in short yes, you just call the getName function and look at the jstring result.

What is JNA vs JNI?

Java Native Access (JNA) is a community-developed library that provides Java programs easy access to native shared libraries without using the Java Native Interface (JNI). JNA's design aims to provide native access in a natural way with a minimum of effort. Unlike JNI, no boilerplate or generated glue code is required.

How does Java JNI work?

The JNI allows Java code that runs within a Java Virtual Machine (VM) to operate with applications and libraries written in other languages, such as C, C++, and assembly. In addition, the Invocation API allows you to embed the Java Virtual Machine into your native applications.

What is JNIEnv * env?

JNIEnv – a structure containing methods that we can use our native code to access Java elements. JavaVM – a structure that lets us manipulate a running JVM (or even start a new one) adding threads to it, destroying it, etc…


1 Answers

When trying your code you could easily add some assertions like this:

JNIEXPORT void JNICALL Java_accessBuffThroughObjectTwo(JNIEnv *env, jobject obj,  jobject objectTwo) {
    jclass clazz;
    jclass bufferClazz;
    jobject bufferJObject;
    jfieldID fid;

    clazz = (*env)->GetObjectClass(env, objectTwo);
    assert(clazz != NULL);
    fid = (*env)->GetFieldID(env, clazz, "obj", "Ljava/lang/Object;");
    assert(fid != NULL);
    bufferJObject = (*env)->GetObjectField(env, javascsicommand, fid);
    assert(bufferJObject != NULL);
    bufferClazz = (*env)->GetObjectClass(env, bufferJObject);
    assert(bufferClazz != NULL);
    fid = (*env)->GetFieldID(env, bufferClazz, "buff", "[B");
    assert(fid != NULL);
}

Doing so you will first see that the first fid will be NULL. That is because the ObjectTwo class does not have any fields of type java.lang.Object. You should change the line to look like this (but add the correct packages instead of com/package):

fid = (*env)->GetFieldID(env, clazz, "obj", "Lcom/package/ObjectOne;");

If you run again you will find that the fid is no longer null and the assertion will pass.

As others have suggested I believe that the javascsicommand should be objectTwo.

Now the next place where the assertion will fail is on bufferJObject. That is because the field exists but the object is NULL and if you check your java code you will notice that the obj field is never instantiated and is null.

Change your java code to something like this:

public class ObjectTwo
{
    private ObjectOne obj = new ObjectOne();
    ...
    ...
}

You will now pass the assertion and even pass all other assertions.

To summarize you were accessing a null object and trying to invoke reflection on it:

bufferClazz = (*env)->GetObjectClass(env, bufferJObject); <-- The bufferJObject was NULL
like image 175
maba Avatar answered Sep 28 '22 05:09

maba