简体   繁体   中英

JNA (Java): How to pass an array of pointers to struct?

I'm using JNA to call into a C library from Java.

In my C code I have:

   void printStructArray( SomeStruct **someStruct, int arraySize );

This expects an array of pointers to struct, ie the method does this:

void printStructArray( SomeStruct **someStruct, int arraySize ) {
   for( int i = 0; i < arraySize; i++ ) {
      cout << "someStruct: " << someStruct[i]->aLong << " " << someStruct[i]->aString << " " << someStruct[i]->aDouble << endl;
   }
}

It's just a toy example, but I have an actual library I want to talk to which needs the same type of argument, but I think using a toy example is easier to explain here?

I've tried all sorts of things, but I'm not sure how to (i) declare this function in JNA (ii) call this function in JNA.

My latest (failed) attempt is:

SomeStruct.byReference[] structs = new SomeStruct.byReference[]{
        new SomeStruct.byReference(123,"hey!",1.23),
        new SomeStruct.byReference(456,"cool!",1.45),
        new SomeStruct.byReference(789,"world!",1.67) };
PointerByReference pointerByReference = new PointerByReference(structs[0].getPointer());
JniTest.instance.printStructArray(pointerByReference, 3);

This causes a SIGSEGV.

Alternatively:

SomeStruct.byReference[] structs = (SomeStruct.byReference[]) new SomeStruct().toArray( new SomeStruct.byReference[]{
        new SomeStruct.byReference(123,"hey!",1.23),
        new SomeStruct.byReference(456,"cool!",1.45),
        new SomeStruct.byReference(789,"world!",1.67) } );
PointerByReference pointerByReference = new PointerByReference(structs[0].getPointer());
JniTest.instance.printStructArray(pointerByReference, 3);

This causes an ArrayStoreException

Tried this also:

SomeStruct.byReference[] structs = new SomeStruct.byReference[]{
    new SomeStruct.byReference(123,"hey!",1.23),
    new SomeStruct.byReference(456,"cool!",1.45),
    new SomeStruct.byReference(789,"world!",1.67) };    JniTest.instance.printStructArray(structs, 3);

With method declared as:

void printStructArray(SomeStruct.byReference[] someStructarray, int num);

This gives '0' as the output from the function, although the good point is it doesn't crash, but it's not giving correct behavior either.

Thoughts?

It's sufficient to pass your array of Structure.ByReference; the address of the array is passed to the native code. JNA automatically allocates the space for the array of pointers, which goes out of scope after the function call.

PointerByReference is intended to pass a pointer value by reference (ie the callee may alter the value). It is not appropriate in this case.

I've just found a solution that works quite nicely, which is to use BridJ instead of JNA. Maybe there is a way to get JNA working, but it doesn't seem obvious. BridJ is really easy to use:

Declaration:

public static native void printStructArray(Pointer<Pointer<SomeStruct > > someStruct, int arraySize);

Usage:

Pointer<Pointer<SomeStruct>> pointers = Pointer.allocatePointers(SomeStruct.class, 3);
pointers.set(0, Pointer.pointerTo( new SomeStruct().aDouble(1.58).aLong(5432).aString(Pointer.pointerToCString("Wheee!")) ) );
pointers.set(1, Pointer.pointerTo( new SomeStruct().aDouble(1.58).aLong(5432).aString(Pointer.pointerToCString("Wheee!")) ) );
pointers.set(2, Pointer.pointerTo( new SomeStruct().aDouble(1.58).aLong(5432).aString(Pointer.pointerToCString("Wheee!")) ) );
JnitestLibrary.printStructArray(pointers, 3 );

It seems they dont support structures by value, but I dont have any structures by value in my current interface, so that's not a problem for me right now. Apparently performance is ok, but I haven't tested performance personally.

Note that I'm still open to JNA solutions, and will tick anyone who can give me a working JNA solution to the problem.

Is this C or C++?

If C, why don't you simply use calloc/malloc to allocate the double pointer for the structure as follows?

somestruct = (SomeStruct **)calloc(arraySize, sizeof(SomeStruct *));

for (i = 0; i < arraySize; i++)

    somestruct[i] = (SomeStruct *)calloc(1, sizeof(SomeStruct));

Now, you can fill the structures as you want.

In the end, I never found a working solution to this. What I did was workaround by writing a wrapper interface, in C, which presented a very simple interface to Java, which was then very easy to link to using JNA, without needing Pointer and so on. This worked quite well, and was less effort than trying to get Pointer working.

So, my interface looked something like:

int createHandle()
void doSomething( int handle )
void releaseHandle( int handle)

It seemed to take a lot less effort to get this working, in terms of time, than trying to create some super complicated jna implementation to directly connect to the underlying c interface.

The other advantage is this meant it was easy to connect to a c++ library, since this way, the c++ interface is wrapped by ac interface, which is easy to connect to.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM