I'm using JNA to communicate with a native C library compiled with Visual Studio 2015. I'm working on a 64-bit machine. I'm trying to receive the value of a C-function through a long pointer long *pdays
argument. I'm getting the following exception:
java.lang.IndexOutOfBoundsException: Bounds exceeds available space : size=4, offset=8 at com.sun.jna.Memory.boundsCheck(Memory.java:220) at com.sun.jna.Memory.getLong(Memory.java:498)
I don't understand what I'm missing here, if I reserve just 4 bytes of memory for the pointer this results in the crash above, however if I reserve 8 everything goes fine. However sizeof(long) returns 4, so why do I need to reserve more than 4 bytes?
System.out.println(NativeLong.SIZE); // --> 4
System.out.println(Native.LONG_SIZE); // --> 4
// Pointer pDays = new Memory(Native.LONG_SIZE); Results in IndexOutOfBoundsException
Pointer pDays = new Memory(8); //
nativeLib.GetDaysUntilExpiration(pDays);
return pDays.getLong(0); // crashes here when reserving just 4 bytes
It crashes because you are trying to read 8 bytes from native memory which has allocated only 4 bytes.
It doesn't matter what the native type is, or that it's only 4 bytes. The Memory
is simply holding 4 bytes that you could interpret in any fashion you wished. You could get a byte[]
array, or an int
(with those 4 bytes) or even a short
or byte
reading only that number of bytes. You could even try a String
(although without a null terminator, you'd probably read much more than the 4 bytes allowed, and who knows what you would get, so that would be dangerous.)
You have asked to get a Java long
which is an 8-byte variable; accordingly, the code checks to see whether the next 8 bytes from your offset fit inside the allocated memory. The code from Memory.java
has this hardcoded:
boundsCheck(offset, 8);
The javadoc is clear about why this is:
Indirect the native pointer to
malloc
space, a laPointer.getLong
. But this method performs a bounds checks to ensure that the indirection does not cause memory outside themalloc
ed space to be accessed.
The correct way to do what you're doing, where you don't manually allocate the space, is to simply use a NativeLongByReference
. JNA will take care of the allocation of space and retrieval of the value on its own, and you won't have to worry about the native size.
NativeLongByReference pDays = new NativeLongByReference();
nativeLib.GetDaysUntilExpiration(pDays);
return pDays.getValue().longValue();
EDIT: I note in your comment you say "The C function argument is a pointer, using a NativeLongByReference would result in a "LongByReference cannot be converted to Pointer" -- this is not an issue with the C function, but with the JNA mapping in your interface. Better to change the JNA mapping of GetDaysUntilExpiration
to take a NativeLongByReference
argument. If you can't change the JNA mapping of that function, you could work around it by using pDays.getPointer()
as the argument.