2012-08-03 109 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