简体   繁体   中英

Why do I need to reserve more than 4 bytes for a JNA pointer to receive a long when sizeof(long) is 4?

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 la Pointer.getLong . But this method performs a bounds checks to ensure that the indirection does not cause memory outside the malloc 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.

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