Click to See Complete Forum and Search --> : JNI : Out parameters, simulating pointers


A. Apvrille
August 17th, 1999, 09:46 AM
Hello,
I'm trying to link some C function with this prototype :
HANDLE myFunc([IN] HWND, [IN] unsigned char *, [OUT] WORD *, [OUT] WORD *, [OUT] unsigned char *)
Don't bother too much about HANDLE, HWND, WORD etc they're just pointer values, longs etc. My actual problem is that my function returns many values (a HANDLE, two
WORDs and an unsigned char -- actually it's a string).

To manage to map pointers on Words, I built up a class Word, with a single parameter value (an int).
In C code (JNI), I therefore need to modify the Word object and set its value. I've read this is possible with SetIntField method and so on.
... but in my case, I can't get it to work (and I can't see my error) : object doesn't seem to be modified... Maybe I haven't got the right fieldID, or the right class, I don't understand...

Here's my code, it isn't long. If someone can help me out...
PS. I've been through the JNI section, in Codeguru, and found an interesting Java Tip of Geoff Friesen, but I seem to be doing it correctly, so I don't understand.

in Word.java

class Word {
public int value;
Word(){}
Word(int i){ value = i; }

}
in GDAPI.java
class GDAPI {
static {
// link to my DLL
System.loadLibrary("gdirect");
}

public native long open(long hWnd,
String szTitle,
Word pIsOk,
Word pIsInit,
String szName //out
);

public static void main(String args[]){
String name = "toto";
Word ok = new Word(0);
Word init = new Word(0);
GDAPI api= new GDAPI();

System.out.println("In JAVA, before call to open");

long h = api.open(0,"Coucou",ok,init,name);

System.out.println("In Java after call :");
System.out.println("\tok="+ok.value);
System.out.println("\tinit="+init.value);
System.out.println("\tname="+name);
System.out.println("\th="+h);
[...]
}
}



In C :
JNIEXPORT jlong JNICALL
Java_GDAPI_open(JNIEnv *env,
jobject obj_this,
jlong hwnd, // in
jstring jstrTitle, // in (not out !)
jobject joIsOk, // only out
jobject joIsInit, // only out
jstring jstrName){ // only out
char *sztitle;
char szname[128];
WORD w_ok,w_init;
HANDLE h;
jclass cls_ok, cls_init;
jfieldID fid_ok,fid_init;

printf("> Java_GDAPI_open\n");

// initialisations
obj_this = obj_this;
h = NULL;
w_ok = 0;
w_init = 0;
sztitle = (char *)(*env)->GetStringUTFChars(env,jstrTitle,0);

// call to real C Function
printf("CFunction(hwnd=%08lx,title=%s,&w_ok=%08lx, &w_init=%08lx,szname=%s)\n",hwnd,sztitle,&w_ok,&w_init,szname);

h = CFunction((HWND) hwnd, (unsigned char*)sztitle, &w_ok, &w_init, (unsigned char*)szname);

printf("returns h=%08lx w_ok=%d w_init=%d szname=%s\n",h,w_ok,w_init,szname);

// updating IsOk object
printf("updating isOk\n");
cls_ok = (*env)->GetObjectClass(env,joIsOk);
fid_ok = (*env)->GetFieldID(env,cls_ok,"value","I");
if (fid_ok == 0) return 0;
(*env)->SetIntField(env,cls_ok,fid_ok, (jint) w_ok);

// updating IsInit
printf("updating IsInit\n");
cls_init = (*env)->GetObjectClass(env,joIsInit);
fid_init = (*env)->GetFieldID(env,cls_init,"value","I");
if (fid_init == 0) return 0;
(*env)->SetIntField(env,cls_init,fid_init, (jint) w_init);

// updating string object
printf("updating string \n");
jstrName = (*env)->NewStringUTF(env,szname);

// free
printf("freeing \n");
(*env)->ReleaseStringUTFChars(env, jstrTitle, sztitle);

printf("< Java_GDAPI_open\n");
return (jlong) h;
}




--
Axelle Apvrille - MSI

A. Apvrille
August 19th, 1999, 10:35 AM
Hello,
Don't worry any longer about my post, I found the error in my code : when you call SetIntField, second parameter is object and not class, whereas when you call SetStaticIntField it's the class.
It seems quite logical, but I hadn't seen my error.
Sorry for those who had a look at that code.

Axelle Apvrille - MSI

Philip Reed
October 12th, 1999, 09:43 AM
I usually do about the same thing, but with the java "wrapper" classes (java.lang.Long, Integer, Float, etc.) instead of doing my own. The procedure for calling it is about the same -- I have to get a jmethodID for the "<init>" method (i.e., the constructor) with signature"(J)V" (for example -- long)

Also, now that I look at it, it seems you could use a short (16-bits) for a WORD, and an int (32-bits) for a DWORD. Somehow I've gotten in the habit of using a java 64-bit long for a DWORD, so I suppose if I'm not wasting great quantities of memory it doesn't matter.

I bought Essential JNI by Rob Gordon, and it seems to have useful "JNI Rules to Remember" that include your particular class/object gotcha, as well as other gotchas that I've hit (e.g., the semicolon after a class name). Those alone, plus a real documentation of the JNI API, make it worth the purchase price.