2012-08-03 102 views
5

我对JNI比较陌生,并且已经弄清了使用JNI在Java对象中整数和数组混乱的基础知识。现在我试图修改/访问Java对象内的Java对象。使用JNI在C中访问Java对象中的Java对象

我一直在寻找互联网和堆栈溢出,还没有找出如何做到这一点。

下面是这个例子。

在Java:

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

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

在JNI,如何从ObjectOne通过ObjectTwo访问 “迷”?我试过这样的事情...

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"); 
} 

对我在做什么有什么帮助错?

+0

第一步是检查每个JNI调用,看看它是否失败。第二:什么是“javascsicommand”? – 2012-08-03 22:51:54

+0

我相信你将'obj'实例的错误值传递给[GetObjectField](http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp16572 )。 查看正确的描述符: 'jobject GetObjectField(JNIEnv * env,jobject obj,jfieldID fieldID);' – oldrinb 2012-08-04 02:55:20

+0

也许你打算通过'objectTwo'而不是'javascsicommand'? – oldrinb 2012-08-04 02:58:59

回答

15

当试图你的代码,你可以很容易地添加一些断言是这样的:

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

这样做,你会先看到第一fid将是NULL。这是因为ObjectTwo类没有任何类型的域java.lang.Object。你应该更改行看起来像这样(但增加,而不是com/package选择正确的软件包):

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

如果再次运行,你会发现,FID不再是零和断言会通过。

正如其他人所建议的,我认为javascsicommand应该是objectTwo

现在断言失败的下一个地方是bufferJObject。这是因为字段存在,但对象为NULL,如果您检查您的java代码,您会注意到obj字段永远不会被实例化,并且是null

更改您的Java代码是这样的:

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

现在,您将通过断言,甚至通过所有其他的断言。

总之你访问一个null对象,并尝试调用它反映:

bufferClazz = (*env)->GetObjectClass(env, bufferJObject); <-- The bufferJObject was NULL 
+0

感谢这个详细的回复,我肯定会利用这个断言并检查NULL,我想你在谈论访问一个空对象的时候碰到了头,我认为这就是给我的访问冲突 – user1575243 2012-08-06 21:37:48

+0

@ user1575243如果您认为我的答案很有用,那么请将答案标记为已接受! – maba 2012-08-07 08:22:33

+1

@ user1575243如果像我这样的人花费一些时间来设置编码环境,启动调试会话并尝试解决其他人的问题,那么您至少可以期望从他们认为他们接受他们认为有用的答案... – maba 2012-08-10 10:45:35