Results 1 to 20 of 21
Thread: Java Native Interface Question
- 11-28-2010, 02:27 PM #1
Member
- Join Date
- Nov 2010
- Posts
- 33
- Rep Power
- 0
Java Native Interface Question
I need to pass an object to a native method. The object contains several int fields and a field that is an int array (contains 32 int).
I have no problem with the int fields of the object, but do not know how to access the int array field of the object in the C code. What approach should be used to get/set values in the int array field of the object passed to the native method?
For int fields in the object passed to the native function I do the following to set the value:
For a field that is an array of int, it seems that I can get the field ID as follows:Java Code:fid = (*env)->GetFieldID(env, cls, "intFieldName", "I"); (*env)->SetIntField(env, myObj, fid, newValue);
The "Get<Type>Field" and "Set<Type>Field" methods can be used for object, boolean, byte, char, short, int, long, float, double. Nothing specific here for array. Should I use the GetObjectField method, if so can the object field then be handled as an array? This is where I am missing something. I have experimented with using the SetIntArrayRegion method but this causes an exception - obviously missing a necessary step here. I am hoping someone here can provide guidance on what I am missing, what JNI functions I should be using, or refer me to the appropriate page of the specification or other document where it is explained.Java Code:fid = (*env)->GetFieldID(env, cls, "arrayFieldName", "[I");
Any suggestions are appreciated.
- 11-28-2010, 02:41 PM #2
- Join Date
- Sep 2008
- Location
- Voorschoten, the Netherlands
- Posts
- 11,413
- Blog Entries
- 7
- Rep Power
- 17
Look in your jni.h file (somewhere in your JDK/include directory); given a jIntArray you can get the elements (all of them or just a region) and you can specifiy whether or not you want your 'env' object to return a copy or the original of the wanted elements.
kind regards,
JosWhen people rob a bank they get a penalty; when banks rob people they get a bonus.
- 11-28-2010, 03:05 PM #3
Member
- Join Date
- Nov 2010
- Posts
- 33
- Rep Power
- 0
Thanks Jos,
If the parameter passed to the native function was an array then I could directly handle it with the Get/SetIntArrayRegion methods.
My problem is that the array is a member of an object passed to the native function.
The object is passed in as a jobject.
I can use the GetObjectClass method to get the corresponding jclass.
I can then get the field ID for a specific member of the object:Java Code:jclass cls; cls = (*env)->GetObjectClass(env, myObjParam);
Once I have the field ID, then (for int fields) I can get or set the value with the Get/SetIntField functions:Java Code:jfieldID fid; fid = (*env)->GetFieldID(env, cls, "myMembField", "I"); // int field
What I don't understand is how to get to the int array member of the object (as a jIntArray) so I can get or set values contained in the array. Any suggestions?Java Code:(*env)->SetIntField(env, myObjParam, fid, newValue);
Thanks again.
- 11-28-2010, 03:27 PM #4
- Join Date
- Sep 2008
- Location
- Voorschoten, the Netherlands
- Posts
- 11,413
- Blog Entries
- 7
- Rep Power
- 17
You can do the same if the field is an array: use your fieldID to get a jobject (which is a jarray finally). The jarray is a jintArray and you use that one to get your regions. (it's all in the jni.h file but you have to search a bit; it's also in the documentation).
kind regards,
JosWhen people rob a bank they get a penalty; when banks rob people they get a bonus.
- 11-28-2010, 03:52 PM #5
Member
- Join Date
- Nov 2010
- Posts
- 33
- Rep Power
- 0
Here is what I am currently doing:
This works fine until I get to the SetIntArrayRegion call, where it crashes. Do you see anything obvious that I am missing or doing wrong?Java Code:jfieldID fid; jclass cls; jintArray arrayObj; jint buf[32]; fid = (*env)->GetFieldID(env, cls, "DATAinfo", "[I"); arrayObj = (*env)->GetObjectField(env, cls, fid); for (i=0; i<32; i++) buf[i] = newValue; // Crashes here - EXCEPTION_ACCESS_VIOLATION (*env)->SetIntArrayRegion(env, arrayObj, 0, 32, buf);
- 11-28-2010, 04:10 PM #6
- Join Date
- Sep 2008
- Location
- Voorschoten, the Netherlands
- Posts
- 11,413
- Blog Entries
- 7
- Rep Power
- 17
I can't see anything wrong from your code snippet. Do the 'fid' and 'cls' fields have sensible values? Also, have you read this part of the documentation?
kind regards,
JosWhen people rob a bank they get a penalty; when banks rob people they get a bonus.
- 11-28-2010, 04:26 PM #7
Member
- Join Date
- Nov 2010
- Posts
- 33
- Rep Power
- 0
Yes the cls and fid variables seem to be fine - they work for int fields with no problems.
Here is how I get the field ID for an int member:
This uses a field descriptor of "I" to indicate int and works fine with the subsequent call to SetIntField.Java Code:fid = (*env)->GetFieldID(env, cls, "intMembName", "I");
Here is how I get the field ID for an int array member:
Note that I use a field descriptor of "[I" to indicate array of int. Do you see any problem with this? This seems to work with the subsequent call to GetObjectField. However, it crashes on the following call to SetIntArrayRegion.Java Code:fid = (*env)->GetFieldID(env, cls, "DATAinfo", "[I");
I do have the document you refer to - I have printed the entire "Java Native Interface Programmer's Guide and Specification" document and have been working through it for the last week or so. Doing OK until I came up against this array issue - this one has got me stumped!
Thanks again.
- 11-28-2010, 05:05 PM #8
- Join Date
- Sep 2008
- Location
- Voorschoten, the Netherlands
- Posts
- 11,413
- Blog Entries
- 7
- Rep Power
- 17
And what about your arrayObj variable? Does it get a sensible value assigned? My guess is that it doesn't ... Is the Java field a static field?
kind regards,
JosWhen people rob a bank they get a penalty; when banks rob people they get a bonus.
- 11-28-2010, 05:06 PM #9
Member
- Join Date
- Nov 2010
- Posts
- 33
- Rep Power
- 0
Jos,
I added more checking on the return values. The GetObjectField call is returning NULL:
The documentation on the GetObjectField function indicates that it works with the native type jobject. I am trying to use this function with a jintArray, which causes it to fail and return NULL.Java Code:fid = (*env)->GetFieldID(env, cls, "DATAinfo", "[I"); if (fid != NULL) { [B]arrayObj = (*env)->GetObjectField(env, cls, fid); // RETURNS NULL[/B] if (arrayObj != NULL) { for (i=0; i<32; i++) buf[i] = myCdp.DATAinfo[i]; // Crashes here - EXCEPTION_ACCESS_VIOLATION (*env)->SetIntArrayRegion(env, arrayObj, 0, 32, buf); } else result = 2004; } else result = 2003;
Is there another way to get a jintArray field from the object?
- 11-28-2010, 05:27 PM #10
- Join Date
- Sep 2008
- Location
- Voorschoten, the Netherlands
- Posts
- 11,413
- Blog Entries
- 7
- Rep Power
- 17
- 11-28-2010, 05:35 PM #11
Member
- Join Date
- Nov 2010
- Posts
- 33
- Rep Power
- 0
The java side seems to be fine. I can print the DATAinfo values:
This works fine, no problem.Java Code:System.out.println("Data0 = " + Integer.toHexString(myRtCdp.DATAinfo[0]));
My theory is that the GetObjectField method is failing and returning NULL because we are passing a jintArray rather than a jobject.
- 11-28-2010, 05:38 PM #12
- Join Date
- Sep 2008
- Location
- Voorschoten, the Netherlands
- Posts
- 11,413
- Blog Entries
- 7
- Rep Power
- 17
- 11-28-2010, 05:47 PM #13
Member
- Join Date
- Nov 2010
- Posts
- 33
- Rep Power
- 0
At the java level, I have instantiated the "myRtCdp" object. This object is passed to the native method as a parameter. This object contains several int members and one int array member (DATAinfo), which is an array of 32 int values.
The native method will get the appropriate values to populate the fields of the object. This works fine for the int fields, but not for the array field - this is where the GetObjectField call returns NULL.
- 11-28-2010, 07:37 PM #14
- Join Date
- Sep 2008
- Location
- Voorschoten, the Netherlands
- Posts
- 11,413
- Blog Entries
- 7
- Rep Power
- 17
- 11-28-2010, 09:18 PM #15
Member
- Join Date
- Nov 2010
- Posts
- 33
- Rep Power
- 0
Thanks for taking the time to look at this issue. It seems like the JNI is well thought out and should have a way to access an array member of an object. I'm sure there is something simple that I am overlooking.
- 11-29-2010, 07:37 AM #16
- Join Date
- Sep 2008
- Location
- Voorschoten, the Netherlands
- Posts
- 11,413
- Blog Entries
- 7
- Rep Power
- 17
Can you show us your Java class with the native method as well as the header part of your native method? We haven't seen it yet and I suspect that the 'cls' parameter refers to the wrong Java object. (I'll get back home in the afternoon (GMT+1) and then I can try it myself).
kind regards,
JosWhen people rob a bank they get a penalty; when banks rob people they get a bonus.
- 11-29-2010, 01:48 PM #17
Member
- Join Date
- Nov 2010
- Posts
- 33
- Rep Power
- 0
I am using JNI in an attempt to create a class library that encapsulates an existing "C" API to allow Java applications to use the API. This API is used with MIL-STD-1553 and ARINC-429 avionics databus interfaces.
Here is my java class. This class corresponds to a "C" struct used by the underlying native API.
Java Code:package altadt; /** * This class represents the 1553 Common Data Packet. * * @author RichWade */ public class CDP { public int NextPtr; public int BMCount; public int APIinfo; public int Rsvd1; public int Rsvd2; public int MaskValue; public int MaskCompare; public int CDPControlWord; public int CDPStatusWord; public int TimeHigh; public int TimeLow; public int IMGap; public int Rsvd3; public int CMD1info; public int CMD2info; public int STS1info; public int STS2info; public int[] DATAinfo; public CDP() { NextPtr = 0; BMCount = 0; APIinfo = 0; Rsvd1 = 0; Rsvd2 = 0; MaskValue = 0; MaskCompare = 0; CDPControlWord = 0; CDPStatusWord = 0; TimeHigh = 0; TimeLow = 0; IMGap = 0; Rsvd3 = 0; CMD1info = 0; CMD2info = 0; STS1info = 0; STS2info = 0; int i; DATAinfo = new int[32]; for (i=0; i<32; i++) DATAinfo[i] = 0; } }
Here is my native method. This calls a function in the underlying native API to read data from a device, then copies the data to the java object. This works fine for the int fields in the object, but the problem is with the int array DATAinfo.
Here is the header:Java Code:JNIEXPORT jint JNICALL Java_altadt_ADTL1_m1553RTSACDPRead(JNIEnv *env, jobject obj, jint devID, jint rtAddr, jint tr, jint subAddr, jint cdpNum, jobject Cdp) { ADT_L0_UINT32 result = ADT_SUCCESS; ADT_L1_1553_CDP myCdp; jfieldID fid; jclass cls; jintArray arrayObj; jint buf[32]; ADT_L0_UINT32 i; memset(&myCdp, 0, sizeof(myCdp)); // Call the underlying API function to get the data requested result = ADT_L1_1553_RT_SA_CDPRead(devID, rtAddr, tr, subAddr, cdpNum, &myCdp); if (result == ADT_SUCCESS) { // Copy the data to the java object cls = (*env)->GetObjectClass(env, Cdp); fid = (*env)->GetFieldID(env, cls, "APIinfo", "I"); (*env)->SetIntField(env, Cdp, fid, myCdp.APIinfo); fid = (*env)->GetFieldID(env, cls, "BMCount", "I"); (*env)->SetIntField(env, Cdp, fid, myCdp.BMCount); fid = (*env)->GetFieldID(env, cls, "CDPControlWord", "I"); (*env)->SetIntField(env, Cdp, fid, myCdp.CDPControlWord); fid = (*env)->GetFieldID(env, cls, "CDPStatusWord", "I"); (*env)->SetIntField(env, Cdp, fid, myCdp.CDPStatusWord); fid = (*env)->GetFieldID(env, cls, "CMD1info", "I"); (*env)->SetIntField(env, Cdp, fid, myCdp.CMD1info); fid = (*env)->GetFieldID(env, cls, "CMD2info", "I"); (*env)->SetIntField(env, Cdp, fid, myCdp.CMD2info); fid = (*env)->GetFieldID(env, cls, "IMGap", "I"); (*env)->SetIntField(env, Cdp, fid, myCdp.IMGap); fid = (*env)->GetFieldID(env, cls, "MaskCompare", "I"); (*env)->SetIntField(env, Cdp, fid, myCdp.MaskCompare); fid = (*env)->GetFieldID(env, cls, "MaskValue", "I"); (*env)->SetIntField(env, Cdp, fid, myCdp.MaskValue); fid = (*env)->GetFieldID(env, cls, "NextPtr", "I"); (*env)->SetIntField(env, Cdp, fid, myCdp.NextPtr); fid = (*env)->GetFieldID(env, cls, "Rsvd1", "I"); (*env)->SetIntField(env, Cdp, fid, myCdp.Rsvd1); fid = (*env)->GetFieldID(env, cls, "Rsvd2", "I"); (*env)->SetIntField(env, Cdp, fid, myCdp.Rsvd2); fid = (*env)->GetFieldID(env, cls, "Rsvd3", "I"); (*env)->SetIntField(env, Cdp, fid, myCdp.Rsvd3); fid = (*env)->GetFieldID(env, cls, "STS1info", "I"); (*env)->SetIntField(env, Cdp, fid, myCdp.STS1info); fid = (*env)->GetFieldID(env, cls, "STS2info", "I"); (*env)->SetIntField(env, Cdp, fid, myCdp.STS2info); fid = (*env)->GetFieldID(env, cls, "TimeHigh", "I"); (*env)->SetIntField(env, Cdp, fid, myCdp.TimeHigh); fid = (*env)->GetFieldID(env, cls, "TimeLow", "I"); (*env)->SetIntField(env, Cdp, fid, myCdp.TimeLow); // HOW TO HANDLE DATAinfo ARRAY??? fid = (*env)->GetFieldID(env, cls, "DATAinfo", "[I"); if (fid != NULL) { arrayObj = (*env)->GetObjectField(env, cls, fid); // RETURNS NULL if (arrayObj != NULL) { for (i=0; i<32; i++) buf[i] = myCdp.DATAinfo[i]; (*env)->SetIntArrayRegion(env, arrayObj, 0, 32, buf); } else result = 2004; } else result = 2003; } return(result); }
Java Code:#include <jni.h> /* Header for class ADTL1 */ #ifndef _Included_ADT_L1_Java #define _Included_ADT_L1_Java #ifdef __cplusplus extern "C" { #endif JNIEXPORT jint JNICALL Java_altadt_ADTL1_InitDevice(JNIEnv *env, jobject obj, jint devID, jint startupOptions); JNIEXPORT jint JNICALL Java_altadt_ADTL1_CloseDevice(JNIEnv *env, jobject obj, jint devID); JNIEXPORT jint JNICALL Java_altadt_ADTL1_GetVersionInfo(JNIEnv *env, jobject obj, jint devID, jintArray peVersion, jintArray layer0ApiVersion, jintArray layer1ApiVersion); JNIEXPORT jint JNICALL Java_altadt_ADTL1_m1553InitDefault(JNIEnv *env, jobject obj, jint devID, jint numIQEntries); JNIEXPORT jint JNICALL Java_altadt_ADTL1_m1553RTInit(JNIEnv *env, jobject obj, jint devID, jint rtAddr); JNIEXPORT jint JNICALL Java_altadt_ADTL1_m1553RTEnable(JNIEnv *env, jobject obj, jint devID, jint rtAddr); JNIEXPORT jint JNICALL Java_altadt_ADTL1_m1553RTStart(JNIEnv *env, jobject obj, jint devID); JNIEXPORT jint JNICALL Java_altadt_ADTL1_m1553RTStop(JNIEnv *env, jobject obj, jint devID); JNIEXPORT jint JNICALL Java_altadt_ADTL1_m1553RTDisable(JNIEnv *env, jobject obj, jint devID, jint rtAddr); JNIEXPORT jint JNICALL Java_altadt_ADTL1_m1553RTClose(JNIEnv *env, jobject obj, jint devID, jint rtAddr); JNIEXPORT jint JNICALL Java_altadt_ADTL1_m1553RTSACDPRead(JNIEnv *env, jobject obj, jint devID, jint rtAddr, jint tr, jint subAddr, jint cdpNum, jobject Cdp); #ifdef __cplusplus } #endif #endif
Here is a section of code from the Java test program that is calling the native function:
Java Code:System.out.print("Reading CDP . . . "); status = api.m1553RTSACDPRead(devID, 1, 0, 1, 0, myRtCdp); if (status == ADTL1.SUCCESS) { System.out.println("Success."); System.out.println("CMD1 = " + Integer.toHexString(myRtCdp.CMD1info)); } else System.out.println("FAILURE - error = " + Integer.toString(status));
Thanks again for taking the time to look at this!
- 11-29-2010, 02:06 PM #18
- Join Date
- Sep 2008
- Location
- Voorschoten, the Netherlands
- Posts
- 11,413
- Blog Entries
- 7
- Rep Power
- 17
That should be this:Java Code:arrayObj = (*env)->GetObjectField(env, cls, fid); // RETURNS NULL
kind regards,Java Code:arrayObj = (*env)->GetObjectField(env, obj, fid); // note: obj, not cls
Jos
ps, I found that here.When people rob a bank they get a penalty; when banks rob people they get a bonus.
- 11-29-2010, 02:20 PM #19
Member
- Join Date
- Nov 2010
- Posts
- 33
- Rep Power
- 0
Jos,
Perfect - that seems to be exactly what my problem was!
I changed my code to:
Now it works! Thanks again for all your help.Java Code:arrayObj = (*env)->GetObjectField(env, [B]Cdp[/B], fid);
- 11-29-2010, 03:39 PM #20
- Join Date
- Sep 2008
- Location
- Voorschoten, the Netherlands
- Posts
- 11,413
- Blog Entries
- 7
- Rep Power
- 17
You're welcome of course; I had to dust off an old laptop of mine to check some of my old code and there I read some comment lines that referred to the reflection mechanism: you need the class info (getClass()) for the definition structs of the fields and methods (your fieldID) and you need the object itself to get the actual field values. I was completely blank again about this as well ;-)
kind regards,
Jos
edit: I forgot this: you might have a look at JNA. It's functionally equivalent to JNI but you don't have to write all those C wrappers.Last edited by JosAH; 11-29-2010 at 04:30 PM.
When people rob a bank they get a penalty; when banks rob people they get a bonus.
Similar Threads
-
why java does not compile to native code?
By docesam in forum New To JavaReplies: 43Last Post: 11-25-2010, 03:54 PM -
Java Native Access (JNA) really simple begginer question
By carek in forum Advanced JavaReplies: 2Last Post: 03-18-2010, 12:39 PM -
Connection to DLL using Java Native Access
By cowboy in forum New To JavaReplies: 1Last Post: 01-23-2010, 08:43 PM -
question on listener interface
By Minu in forum Java ServletReplies: 1Last Post: 01-16-2009, 10:33 AM -
Interface question in java
By tony404 in forum Advanced JavaReplies: 2Last Post: 06-27-2008, 11:47 AM


LinkBack URL
About LinkBacks
Reply With Quote

Bookmarks