Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JNA: Set struct pointer to NULL

I am starting to use JNA to communicate with a device on the RS485 interface of a computer. Suprisingly to me I came to good results very quickly. But now I am stuck by a simple problem. The library I use accepts a pointer to a pointer of struct. The actual signature is

func(Struct1 **, Struct2 **, Struct3 *, Struct4 *, long)

Now to indicate the size of the first parameter the library expects the last pointer to be a NULL pointer. This is what fails. Following code is what I tried so far:

    Struct1.ByReference[] s = (Struct1.ByReference[]) new Struct1.ByReference().toArray(size);
    int pos = 0;

    // ...
    // for loop to set the s[pos] struture values
    for(pos = 0; pos < size - 1; pos++)
    // ...

    // Now set the last array element to a null pointer to indicate end-of-list
    s[pos].getPointer().setPointer(0, null);// Following does not work: results in zero memoried structure
    s[pos] = null; // Following does not work wither: NullPointerException at com.sun.jna.Structure.autoWrite 

EDIT 1

s[pos] = new Struct1.ByReference(Pointer.NULL); // results in zero memoried structure as well

EDIT 2

According to technomage's question. If I were to write C code it would probably look something like that:

Struct1 **s = malloc(n * sizeof(Struct1*));

for(int i=0; i<n; i++)
{  
   if(i == n -1)
   {
      s[i] = NULL;
   }
   else
   {
      s[i] = malloc(sizeof(Struct1));
      s[i].bla = value;
      ....
   }
}

But be warned: I am not very skilled in C/C++. I consider Java to be my domain.

Has anyone had a similar problem? Maybe I am just not seeing the wood for the trees...

Thanks in advance.

like image 554
Sebastian Götz Avatar asked Nov 09 '22 18:11

Sebastian Götz


1 Answers

Structures in JNA are pointers, so what you really need here is a pointer to a (pointer to a) Structure, which is a PointerByReference -- in your case, an array of them.

Given the code example above, you'll create your array of Structures, one less than n:

Struct1[] struct1Array = new Struct1[n-1];

This only allocates the Java memory for the array.

Next you'll instantiate and write the changes you make to native memory:

for (int i = 0; i < n-1; i++) {
    struct1Array[i] = new Struct1();
    struct1Array[i].bla = value;
    struct1Array[i].write();
}

The new Struct1() allocates native side memory for these structures. It's possible to use the Structure.toArray() method to do this as well; I'm intentionally doing this a bit more manual and low-level to try to make clear what's happening.

Then you'll create a corresponding PointerByReference array to hold the pointers to these structures. You'll add an extra element for the null:

PointerByReference[] pbrArray = new PointerByReference[n];

Again, this is only java-side allocation. And then you fill it with pointers to the pointers to the structure, obtained from the Structure.getPointer() method:

for (int i = 0; i < n-1; i++) {
    pbrArray[i] = new PointerByReference(struct1Array[i].getPointer());
}
pbrArray[n - 1] = new PointerByReference(Pointer.NULL);

The new PointerByReference() here allocates the native side memory for the pointer itself, which points to the native-side structure you allocated earlier.

From how I understand your initial question, you will pass this PointerByReference array to your function, which presumably updates your structures.

Since you created the two arrays in this fashion, you can keep track of their correspondence by array index. You may have to iterate through the structure array and read() the native memory into the Java-side structure to do further processing with it. Typically when you work directly with Structures being passed to methods they autowrite and autoread, but when using a PointerByReference to indirectly reference the Structure, JNA isn't as friendly.

As an alternative to tracking the two arrays by corresponding indices, you could "forget" the initial Structure assignment and recover it later using the PointerByReference.getValue() method on your array to recover a pointer to the memory for the structure, and then instantiate a new structure using that Pointer in its constructor (e.g. new Struct1(pbr.getValue()) which calls super() with that pointer).

like image 99
Daniel Widdis Avatar answered Nov 14 '22 21:11

Daniel Widdis