aspose file tools*
The moose likes Other JSE/JEE APIs and the fly likes Unable to pass jlongArray back to Java Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Other JSE/JEE APIs
Bookmark "Unable to pass jlongArray back to Java" Watch "Unable to pass jlongArray back to Java" New topic
Author

Unable to pass jlongArray back to Java

David M Fairchild
Greenhorn

Joined: Apr 08, 2005
Posts: 18
I'm trying to return an array of long values to Java to process. I believe the problem is in the C++ code. Here is the function definition:

JNIEXPORT jlongArray JNICALL Java_papers_pi_PiAbsConn_pipt_1location
(JNIEnv *env, jobject obj, jlong pt) {
long arLocs[5];

jlongArray buf = env->NewLongArray(5);
if(buf == NULL) {
return NULL; //Out of Memory
}

long ret = pipt_location(pt, arLocs);
if(ret == 0){
for(int i=0;i<5;i++) {
printf("Location %d is %ld.\n", i, arLocs[i]);
env->SetLongArrayRegion(buf, i, 1, (const jlong*)&arLocs[i]);
}
}

return buf;
}

The printf statement verifies that I got the correct data from the pipt_location call. The code compiles. But when I get back to Java the array appears to have all 0s in it. Here is the java method:

public abstract class PiAbsConn {
// Native function calls
...
public native long[] pipt_location(long pt);
...
}

public class PiConn extends PiAbsConn {
...
public long GetLocations(long pt, long arLocs[]) {
long ret = 0;

try {
arLocs = pipt_location(pt);
} catch(Exception e) {
ret = e.hashCode();
}

return ret;
}
...
}

public class PiTag extends PiConn {
private long[] mlngLocations;
...
private void FindAttributes(long pipt) {
long arLocs[] = new long[5];

// Locations
try {
if(GetLocations(pipt, arLocs) == 0) {
System.out.println("GetLocations(long, long[]) suceeded.");
for(int i=0;i<5;i++) {
System.out.println("arLocs[" + i + "] = " + arLocs[i]);
mlngLocations[i] = arLocs[i];
}
}
} catch (NullPointerException e) {
mlngStatus = e.hashCode();
System.out.println("No valid values returned. Error " + mlngStatus);
}
}
...
}

I've ellipsed out the code that isn't relavent to the question. Here is the output:

Called SetTagName.
Looking for pipt for Test.
Returned 76820 as pipoint.
Called pipt_location for point 76820.
Location 0 is 22.
Location 1 is 235.
Location 2 is 0.
Location 3 is 1.
Location 4 is 0.
GetLocations(long, long[]) suceeded.
arLocs[0] = 0
arLocs[1] = 0
arLocs[2] = 0
arLocs[3] = 0
arLocs[4] = 0
Location1 of tag Test is 0

I've tried a couple of variations on the line setting the array values to be passed back to Java.

env->SetLongArrayRegion(buf, i, 1, arLocs[i]); (won't compile)
env->SetLongArrayRegion(buf, i, 1, &arLocs[i]); (won't compile)

and a few other wierd ones.

Any suggestions would be appreciated.

Thanks,
Dave
Ernest Friedman-Hill
author and iconoclast
Marshal

Joined: Jul 08, 2003
Posts: 24183
    
  34

Hi Dave,

Well, here's a problem in the Java code:



you're assigning the array returned from the native function to a local variable. Java is pass-by-value, so that new value disappears when GetLocations returns.

Have GetLocations return null on error, and the array from the native method on success. Better yet, remove a layer and have FindAttributes call the native method directly, and deal with any exceptions. Also, have the native methods throw Java exceptions rather than returning null -- it's more idiomatic and would simplify your code a lot.


[Jess in Action][AskingGoodQuestions]
David M Fairchild
Greenhorn

Joined: Apr 08, 2005
Posts: 18
Thanks for your reply. I have always had difficulty understanding Java parameter passing. From API programming I'm acustomed to an algorythm that goes like this:

ret = myfunc(inputs, answer);
if(ret <> 0) {
do error routine;
}
...

It seems like the preferred Java algorythm is:

try {
answer = myfunc(inputs);
...
} catch(Exception e) {
do error routine;
}

Let me know if I'm off track on this. I've re-written the Java portion of my code to follow the above example. I am still not getting the array returned from the native function though. And I don't know much about having the native code (c++ in this case) throw a Java Exception. I did try to reason it out from the chapter on exceptions in the book ("The Java Native Interface Programmers Guide and Specification", Sheng Liang, ADDISON-WESLEY).

So, now I'm back to my orginal problem, I can't get the native code to return the array. I'm guessing this may have to do with where the memory for the array is allocated.

One of the other native functions in the interface returns a string with no probems. And a string is nothing but an array of characters:

JNIEXPORT jstring JNICALL Java_papers_pi_PiAbsConn_pipt_1exdesc
(JNIEnv *env, jobject obj, jlong pt) {
char szEDesc[255];
jlong len = 255;

memset(szEDesc, '\0', sizeof(szEDesc));

printf("Called pipt_exdesc for point %ld.\n", pt);
if(pipt_exdesc(pt, szEDesc, len) == 0) {
return env->NewStringUTF(szEDesc);
} else {
return NULL;
}
}

(I know I need to go through this code and add provisions for throwing Java Exceptions.) But, as you see, the memory for the character array is originally allocated in the native code. But I believe the statement "return env->NewStringUTF(szEDesc);" caused the jvm to allocate memory for the string.

In the case of the array of longs, the statement "jlongArray buf = env->NewLongArray(5);" should be allocating the memory in the jvm. In the end I'm still confussed and I don't have my data.

Help!
Ernest Friedman-Hill
author and iconoclast
Marshal

Joined: Jul 08, 2003
Posts: 24183
    
  34


It seems like the preferred Java algorythm is:


Yes, you understand perfectly.

As far as helping you figure out this problem: put a simple print loop in the Java method GetLocations(), right after you call the native method, to print each element of the array. You should find that you're getting the values into that variable. But the point is that assigning to that method parameter changes nothing outside of the GetLocations() routine. You need to be saying



Is that what you're doing now?
David M Fairchild
Greenhorn

Joined: Apr 08, 2005
Posts: 18
This is my current GetLocations function definition:

public long[] GetLocations(long pt) throws Exception {
long ret = 0;
long arLocs[] = new long[5];

try {
arLocs = pipt_location(pt);
for(int i=0;i<5;i++) {
System.out.println("arLocs[" + i + "] = " + arLocs);
}
} catch(Exception e) {
arLocs = null;
Throw e;
}

return arLocs;
}

This is the output of the program:

JNI version is 65540
Called SetTagName.
Looking for pipt for Test.
Returned 76820 as pipoint.
Called pipt_location for point 76820.
Location 0 is 22.
Location 1 is 235.
Location 2 is 0.
Location 3 is 1.
Location 4 is 0.

[i]arLocs[0] = 1009317314582
arLocs[1] = 0
arLocs[2] = 0
arLocs[3] = 0
arLocs[4] = 0

GetLocations(long, long[]) suceeded.
Called pipt_exdesc for point 76820.
Called pipt_instrumenttag for point 76820.
Called pipt_pointtypex for point 76820.
PointType = 11
Location1 of tag Test is 1009317314582

The bold portion is output from the native code usint printf, the italics portion is output from the GetLocations function in Java.

The values output by the native code are correct. Those output by the java code are wrong. However, you will notice that Location1 (which is arLoc[0]) does return correctly. So I think all of the code within Java is correct. But I can't pass the correct values back to Java from the native code.

I altered the return statment in the pipt_location function to:

env->SetLongArrayRegion(buf, 0, 1, (jlong*)&arLocs);

based on an example I found in the book. This appears to have loaded only the first value of the array. Note that both 'buf' and 'arLocs' are arrays. arLocs is a c array of long while buf is of type jlongArray, created by the statement: "jlongArray buf = env->NewLongArray(5);"
David M Fairchild
Greenhorn

Joined: Apr 08, 2005
Posts: 18
Got It !!!

The problem seem to be in how Java was interpreting the c type long. I had a statment:

env->SetLongArrayRegion(buf, i, 1, (jlong*)&arVal[i]);

Which compiled but transferred garbage back to Java. So I added a variable:

jlong lVal = 0;

and then converted arVal[i] to a jlong:

iVal = (jlong)arVal[i];

Then added that to the jlongArray:

env->SetLongArrayRegion(buf, i, 1, &lVal);

And Viola' ... It worked!

Thanks for the help.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Unable to pass jlongArray back to Java