Passing a Java class into a void* parameter with JNA

Iain Sproat picture Iain Sproat · Nov 4, 2010 · Viewed 8.4k times · Source

I have a function in C which I'm trying to call from Java with JNA:

int myCfunc(void *s, int *ls);

According to the JNA documentation the void* requires a com.sun.jna.Pointer be passed to the function. In java with JNA I believe the above function will be wrapped as follows:

public interface myWrapper extends Library{ 
    public int myCfunc(Pointer s, IntByReference ls);
}

The objects which need to linked to the Pointer, and passed in parameter s will be classes which implement JNA Structure, such as:

public class myClass extends Structure{
    public int x;
    public int y;
    public int z;
}

Unfortunately, the parameter ls is an integer representing the length of the class in bytes. Java doesn't have a sizeof function, so this adds an extra bit of complexity. The other major problem I'm having is ensuring I'm correctly passing the contents of the object to native memory and back.

my code is similar to the following:

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.ptr.IntByReference;

public void foo(){
    myWrapper wrapper = (myWrapper) Native.loadLibrary("SomeDllWithLegacyCode", myWrapper.class);

    myClass myObj = new myClass();
    myObj.x = 1;
    myObj.y = 2;
    Pointer myPointer = myObj.getPointer();

    int size = Native.getNativeSize(myClass.class);
    IntByReference len = new IntByReference(size);

    myObj.write(); //is this required to write the values in myObj into the native memory??
    wrapper.myCfunc(myPointer, len);
    myObj.read();  //does this read in the native memory and push the values into myObj??

    myPointer.clear(size); //is this required to clear the memory and release the Pointer to the GC??
}

I'm getting an error that the data passed is of a larger size than is expected by the C function.

The above code roughly following the same sort of steps as provided out in this answer to a question dealing with a similar issue, but in C#. I've tried and tested that it works in C#.

My question is similar to another on Stackoverflow, but that deals with a Pointer to an IntByReference rather than a pointer to a class.

Answer

Cerber picture Cerber · Dec 24, 2010

First of all JNA handle automatically it's own memory allocations which means that to following line is useless (and can damage the memory stack) :

myPointer.clear(size); //is this required to clear the memory and release the Pointer to the GC??

Next it also automatically deal with native pointer types which means that both lines below are equivalent in your case :

public int myCfunc(Pointer s, IntByReference ls);
public int myCfunc(myClass s, IntByReference ls);

and thus JNA will do myObj.write(); and read for you.

The following is 100% correct but I suggest that you log len.getValue() before and after your call to myCfunc (which sould give 3*4=12 ; 3 int of 4 bytes) :

int size = Native.getNativeSize(myClass.class);
IntByReference len = new IntByReference(size);

If all of this is correct then you probably have an error in your structure's prototype.

From my experience this is mostly due to an outdated C header file or an outdated library :

  • You are using header version 2.0 which states that struct's values are int but your linking against library v1.0 which takes a bytes structure
  • You are using header version 2.0 which states that struct's values are int but your linking against library v1.9 which takes two ints and a byte

in the end your code should look like this :

public void foo(){
    myWrapper wrapper = (myWrapper) Native.loadLibrary("SomeDllWithLegacyCode", myWrapper.class);

    myClass myObj = new myClass();
    myObj.x = 1;
    myObj.y = 2;

    int size = Native.getNativeSize(myClass.class);
    IntByReference len = new IntByReference(size);

    //log len.getValue
    wrapper.myCfunc(myObj, len);
    //log len.getValue
}

You can also try to voluntarily decrease len's value for debugging purposes eg :

IntByReference len = new IntByReference(size-1);
IntByReference len = new IntByReference(size-2);
IntByReference len = new IntByReference(size-3);
//...
IntByReference len = new IntByReference(size-11);

This won't do what you wan't but at least it should give you the correct "max len"