I’ve been using JNA with relative success to make native function calls from Java to a small C library that I wrote. Passing structures or pointers from one to the other works great once you’ve worked out the tricks of structure mapping, memory management and passing by reference.
I am now trying to pass, from Java to C, an array of structures. Here is the C code for the structure:
typedef struct key {
int length;
void *data;
} key_t;
I have the matching definition in Java:
public class Key extends Structure {
public int length;
public Pointer data;
public Key() {
this.setFieldOrder(new String[] {"length", "data"});
}
public void setAsLong(long value) {
this.length = 8;
this.data = new Memory(this.length);
this.data.setLong(0, value);
}
public long longValue() {
return this.data != null ? this.data.getLong(0) : Long.MIN_VALUE;
}
};
If I understood the documentation and what I read online, I need to create my array as a contiguous memory section by doing the following on the Java side:
Key[] keys = new Key().toArray(2);
for (int i=0; i<2; i++) {
k.setAsLong(42+i);
}
So far so good. If I dump the content of each Key structure in Java using Structure.toString(), everything is in here as expected. Note that the code about setting as a long value, allocating memory for the key’s content, etc, work fine when I pass a single Key structure from Java to C. So here I pass my array to my native function by using the pointer to the first element of the array:
instance.foo(keys[0].getPointer(), keys.length);
My C function is of course defined like this:
void foo(key_t *keys, size_t count) {
...;
}
The array gets there correctly: the keys pointer on the C side has the same address as keys[0].getPointer() in Java, but unfortunately the members of each structure in the array are 0/NULL, as pointed out by GDB:
(gdb) print keys
$1 = (key_t *) 0x7fd7e82389e0
(gdb) print keys[0]
$2 = {length = 0, data = 0x0}
At this point I honestly have no clue what’s going on. As I said, if I pass just one structure, it works fine, but here no way. The only difference I can see is the Java native method signature which uses Pointer instead of Key[] but when I use the array I get:
IllegalArgumentException: [Lfoo.bar.Key; is not a supported argument type (in method foo ...
Thanks
If you pass a
Pointervalue, JNA has no idea that you’re actually passing aStructureor an array of them, and it’s up to you to ensure you callStructure.write()before andStructure.read()after the native call.If you pass either a
StructureorStructure[], then JNA will take care of the synchronization automagically. In the case ofStructure, JNA uses internal bookkeeping to determine whether the structure you’re passing is at the head of an array of structures.