Reputation: 63
I am trying to invoke a function in a C DLL that has a function that takes a pointer to a struct. This struct (Data
) is defined like this:
struct BD
{
char* Data;
int Length;
};
struct EE
{
char* Key;
BD* Value;
};
struct Data
{
char* Name;
BD* Picture;
// A NULL-terminated array of pointers to EE structures.
EE** Elements;
};
In Java I have defined some classes that look like this:
public static class BD extends Structure implements Structure.ByReference {
public byte[] Data;
public int Length;
}
public static class EE extends Structure implements Structure.ByReference {
public String Key;
public BD Value;
}
public static class Data extends Structure {
public String Name;
public BD Picture;
public PointerByReference Elements;
}
But now I don't know exactly how to populate the Data
object correctly. I think I can figure out the Name
and Picture
fields but what do I set the Elements
field to? I can create a Java array of EE
objects but then how do I get a PointerByReference from that? Maybe I need to declare Elements
as a Pointer[]
but then would I just fill in each element of the array with the getPointer()
for each EE
object? That doesn't quite seem right, though?
Edit: To give a better idea of what I'm trying to do:
Data data = new Data();
// Fill in Name and Picture fields.
EE[] elements = new Elements[10];
// Fill in the elements array.
// Now how do I set the Elements field on the data object from the elements array?
data.Elements = ???
Edit2: Here's how I solved it with technomage's help:
I changed my Data
structure to look like this:
public static class Data extends Structure {
public String Name;
public BD Picture;
public Pointer Elements;
}
And my BD
structure to look like this:
public static class BD extends Structure implements Structure.ByReference {
public Pointer Data;
public int Length;
}
To convert a Java byte[]
to a JNA Pointer
I had to use a ByteBuffer
:
ByteBuffer buf = ByteBuffer.allocateDirect(bytes.length);
buf.put(bytes);
bd.Data = Natvie.getDirectBufferPointer(buf);
JNA doesn't like ByteBuffer
s in a struture unfortunately.
To get my elements pointer, I needed to create an array of Pointer
s to each EE
object (see technomage's answer for PointerArray
implementation):
EE e = new EE();
// Populate e object.
// ...
// Important: ensure that the contents of the objects are written out to native memory since JNA can't do this automatically
e.write();
ptrs.add(e);
// Once each object is setup we can simply take the array of pointers and use the PointerArray
data.Elements = new PointerArray(ptrs.toArray(new Pointer[0]));
I couldn't use ByteBuffer
or PointerArray
directly in the structure definition so I had to rely on Pointer
.
Upvotes: 1
Views: 1827
Reputation: 10069
Make Elements
a Pointer
, then use Pointer.getPointerArray(0)
to retrieve the structure pointers of interest.
You'll then need to initialize each Structure
from each retrieved Pointer
(don't forget to call Structure.read()
).
There is no way to automagically perform the indirection on the pointer and wind up with an array of Structure
, but it's simple enough to encode into a method on your Structure
class (e.g. Data.getElements()
).
EDIT
Sample pointer array implementation, to initialize the pointer array on write (rather than read):
class PointerArray extends Memory {
private final Pointer[] original;
public PointerArray(Pointer[] arg) {
super(Pointer.SIZE * (arg.length+1));
this.original = arg;
for (int i=0;i < arg.length;i++) {
setPointer(i*Pointer.SIZE, arg[i]);
}
setPointer(Pointer.SIZE*arg.length, null);
}
}
}
You'll also need to call Structure.write()
before calling the native function (you might do this as part of PointerArray
), since JNA doesn't know that the memory needs to be synched when you use a vanilla Pointer
instead of Structure
.
Upvotes: 2