首页 > 解决方案 > 当 sizeof(long) 为 4 时,为什么我需要为 JNA 指针保留超过 4 个字节才能接收 long?

问题描述

我正在使用 JNA 与使用 Visual Studio 2015 编译的本机 C 库进行通信。我正在使用 64 位机器。我试图通过长指针long *pdays参数接收 C 函数的值。我收到以下异常:

java.lang.IndexOutOfBoundsException:边界超出可用空间:com.sun.jna.Memory.boundsCheck(Memory.java:220) 处的大小 = 4,偏移量 = 8,位于 com.sun.jna.Memory.getLong(Memory.java: 498)

我不明白我在这里缺少什么,如果我只为指针保留 4 个字节的内存,这会导致上面的崩溃,但是如果我保留 8 个,一切都会好起来的。然而 sizeof(long) 返回 4,那为什么我需要保留超过 4 个字节呢?

    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

标签: javajna

解决方案


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 malloced 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.


推荐阅读