当 sizeof(long) 为 4 时,为什么我需要为 JNA 指针保留超过 4 个字节才能接收 long?
Why do I need to reserve more than 4 bytes for a JNA pointer to receive a long when sizeof(long) is 4?
我正在使用 JNA 与使用 Visual Studio 2015 编译的本机 C 库进行通信。我正在使用 64 位计算机。我正在尝试通过长指针 long *pdays
参数接收 C 函数的值。我收到以下异常:
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)
我不明白我在这里遗漏了什么,如果我只为指针保留 4 个字节的内存会导致上面的崩溃,但是如果我保留 8 个字节则一切正常。但是sizeof(long) returns 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
它崩溃是因为您试图从只分配了 4 个字节的本机内存中读取 8 个字节。
无论原生类型是什么,或者只有 4 个字节都没有关系。 Memory
仅包含 4 个字节,您可以按照您希望的任何方式进行解释。您可以得到一个 byte[]
数组,或一个 int
(具有这 4 个字节)甚至 short
或 byte
只读取该字节数。您甚至可以尝试 String
(尽管没有空终止符,您可能会读取比允许的 4 个字节更多的内容,谁知道您会得到什么,所以这很危险。)
您要求获取一个Java long
,这是一个8字节的变量;因此,代码会检查偏移量的下一个 8 个字节是否适合分配的内存。来自 Memory.java
的代码具有以下硬编码:
boundsCheck(offset, 8);
javadoc 很清楚这是为什么:
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.
不手动分配 space 的正确方法是简单地使用 NativeLongByReference
。 JNA 会自行处理 space 的分配和值的检索,您不必担心原始大小。
NativeLongByReference pDays = new NativeLongByReference();
nativeLib.GetDaysUntilExpiration(pDays);
return pDays.getValue().longValue();
编辑:我在你的评论中注意到你说 "The C function argument is a pointer, using a NativeLongByReference would result in a "LongByReference cannot be converted to Pointer”——这不是 C 函数的问题,而是接口中 JNA 映射的问题。最好更改GetDaysUntilExpiration
的 JNA 映射采用 NativeLongByReference
参数。如果您不能更改该函数的 JNA 映射,则可以通过使用 pDays.getPointer()
作为参数来解决它。
我正在使用 JNA 与使用 Visual Studio 2015 编译的本机 C 库进行通信。我正在使用 64 位计算机。我正在尝试通过长指针 long *pdays
参数接收 C 函数的值。我收到以下异常:
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)
我不明白我在这里遗漏了什么,如果我只为指针保留 4 个字节的内存会导致上面的崩溃,但是如果我保留 8 个字节则一切正常。但是sizeof(long) returns 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
它崩溃是因为您试图从只分配了 4 个字节的本机内存中读取 8 个字节。
无论原生类型是什么,或者只有 4 个字节都没有关系。 Memory
仅包含 4 个字节,您可以按照您希望的任何方式进行解释。您可以得到一个 byte[]
数组,或一个 int
(具有这 4 个字节)甚至 short
或 byte
只读取该字节数。您甚至可以尝试 String
(尽管没有空终止符,您可能会读取比允许的 4 个字节更多的内容,谁知道您会得到什么,所以这很危险。)
您要求获取一个Java long
,这是一个8字节的变量;因此,代码会检查偏移量的下一个 8 个字节是否适合分配的内存。来自 Memory.java
的代码具有以下硬编码:
boundsCheck(offset, 8);
javadoc 很清楚这是为什么:
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.
不手动分配 space 的正确方法是简单地使用 NativeLongByReference
。 JNA 会自行处理 space 的分配和值的检索,您不必担心原始大小。
NativeLongByReference pDays = new NativeLongByReference();
nativeLib.GetDaysUntilExpiration(pDays);
return pDays.getValue().longValue();
编辑:我在你的评论中注意到你说 "The C function argument is a pointer, using a NativeLongByReference would result in a "LongByReference cannot be converted to Pointer”——这不是 C 函数的问题,而是接口中 JNA 映射的问题。最好更改GetDaysUntilExpiration
的 JNA 映射采用 NativeLongByReference
参数。如果您不能更改该函数的 JNA 映射,则可以通过使用 pDays.getPointer()
作为参数来解决它。