如何将指针映射到数组?我不断收到无效的内存访问或垃圾数据
How to JNA-map a pointer-to-array? I keep getting Invalid Memory Access or garbage data
我正在尝试使用 JNA 在 Leap Motion's C API (LeapC) 和 Java 之间架起桥梁,因为 Leap Motion 已经弃用了他们的官方 Java 绑定。但是,我对 C 不熟悉,以前也没有使用过 JNA。到目前为止,我已经设法让一些事情开始工作(比如连接到我的 Leap Motion 控制器并轮询它的状态事件),但在接收实际跟踪数据时我遇到了困难。
LeapC 的相关结构在 LeapC.h 中定义如下:
/** \ingroup Structs
* A snapshot, or frame of data, containing the tracking data for a single moment in time.
* The LEAP_FRAME struct is the container for all the tracking data.
* @since 3.0.0
*/
typedef struct _LEAP_TRACKING_EVENT {
LEAP_FRAME_HEADER info;
int64_t tracking_frame_id;
/** The number of hands tracked in this frame, i.e. the number of elements in
* the pHands array.
*/
uint32_t nHands;
/**
* A pointer to the array of hands tracked in this frame.
*/
LEAP_HAND* pHands;
/**
* Current tracking frame rate in hertz.
*/
float framerate;
} LEAP_TRACKING_EVENT;
我在 JNA 中实现它的尝试只取得了部分成功:info
、tracking_frame_id
和 nHands
字段被正确读取并包含预期信息。 pHands
拒绝工作。
尝试 1
首先,我尝试将 pHands
视为指向 LEAP_HAND 数组开头的指针:
@FieldOrder({ "info", "tracking_frame_id", "nHands", "pHands", "framerate" })
public static class LEAP_TRACKING_EVENT extends Structure
{
public LEAP_FRAME_HEADER info;
public long tracking_frame_id;
public int nHands;
public Pointer pHands;
public float framerate;
}
尝试从指针指向的地址读取任何内容,无论是完整的结构(new LEAP_HAND(pHands)
)还是只是一个字节(pHands.getByte()
)总是会从 JNA 得到无效的内存访问错误.
根据阅读各种其他 Whosebug 问题和答案,我似乎应该这样做。
尝试 2
其次,我尝试将 pHands
视为直立数组:
@FieldOrder({ "info", "tracking_frame_id", "nHands", "pHands", "framerate" })
public static class LEAP_TRACKING_EVENT extends Structure
{
public LEAP_FRAME_HEADER info;
public long tracking_frame_id;
public int nHands;
public LEAP_HAND[] pHands = new LEAP_HAND[1]; //Size-limited to 1 here while testing.
public float framerate;
}
假设只存在一只手(如上面的 JNA 代码中所定义),数组由 LEAP_HAND 的实例填充,但它包含垃圾数据(主要是零)。奇怪的是,当我沿着 x 轴移动手时,pHands[0].palm.position.z
的值确实发生了变化,因此它似乎与此处的正确内存部分部分重叠,但未正确对齐。
我一定是在读取这个数组时做错了什么。有人知道我错过了什么吗?
编辑:来自 SDK 示例之一的工作 C 代码
他们这样称呼 LeapPollConnection
:result = LeapPollConnection(connectionHandle, timeout, &msg);
。如果 msg
包含跟踪事件,他们就会调用 handleTrackingEvent(msg.tracking_event);
。该函数定义如下:
//File: ExampleConnection.c
static void handleTrackingEvent(const LEAP_TRACKING_EVENT *tracking_event){
if(ConnectionCallbacks.on_frame){
ConnectionCallbacks.on_frame(tracking_event);
}
}
ConnectionCallbacks.on_frame
绑定到以下 OnFrame
函数:
//File: CallbackSample.c
/** Callback for when a frame of tracking data is available. */
static void OnFrame(const LEAP_TRACKING_EVENT *frame){
printf("Frame %lli with %i hands.\n", (long long int)frame->info.frame_id, frame->nHands);
for(uint32_t h = 0; h < frame->nHands; h++){
LEAP_HAND* hand = &frame->pHands[h];
printf(" Hand id %i is a %s hand with position (%f, %f, %f).\n",
hand->id,
(hand->type == eLeapHandType_Left ? "left" : "right"),
hand->palm.position.x,
hand->palm.position.y,
hand->palm.position.z);
}
}
编辑 2:目前所有(相关)代码:
首先是轮询和获取事件的代码。
// Define an object to receive an event message into.
LEAP_CONNECTION_MESSAGE.ByReference messageRef = new LEAP_CONNECTION_MESSAGE.ByReference();
while (true)
{
// Poll LeapC for an event. A status code for success is returned,
// and messageRef is populated with the event message.
LeapC.INSTANCE.LeapPollConnection(leapConnection.getValue(), 500, messageRef);
// If the event is a tracking event, get the event data.
if (messageRef.type == eLeapEventType.Tracking.getShortValue())
{
LEAP_TRACKING_EVENT event = messageRef.union.tracking_event;
}
// Sleep a moment before polling again.
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
}
}
下面是 PollLeapConnection
和 LEAP_CONNECTION_MESSAGE
的实现。
LeapPollConnection (LeapC API ref)
// Original C signature:
// LeapPollConnection(LEAP_CONNECTION hConnection, uint32_t timeout, LEAP_CONNECTION_MESSAGE* evt);
public eLeapRS LeapPollConnection(Pointer hConnection, int timeout,
LEAP_CONNECTION_MESSAGE.ByReference message);
LEAP_CONNECTION_MESSAGE (LeapC API ref)
// Original C signature:
// typedef struct _LEAP_CONNECTION_MESSAGE {
// /**
// * The size of this message struct. @since 3.0.0
// */
// uint32_t size;
//
// /**
// * The message type. @since 3.0.0
// */
// eLeapEventType type;
//
// /**
// * A pointer to the event data for the current type of message. @since 3.0.0
// */
// union {
// /** An untyped pointer. @since 3.0.0 */
// const void* pointer;
// /** A connection message. @since 3.0.0 */
// const LEAP_CONNECTION_EVENT* connection_event;
// /** A connection lost. @since 3.0.0 */
// const LEAP_CONNECTION_LOST_EVENT* connection_lost_event;
// /** A device detected message. @since 3.0.0 */
// const LEAP_DEVICE_EVENT* device_event;
// /** A device's status has changed. @since 3.1.3 */
// const LEAP_DEVICE_STATUS_CHANGE_EVENT* device_status_change_event;
// /** A policy message. @since 3.0.0 */
// const LEAP_POLICY_EVENT* policy_event;
// /** A device failure message. @since 3.0.0 */
// const LEAP_DEVICE_FAILURE_EVENT* device_failure_event;
// /** A tracking message. @since 3.0.0 */
// const LEAP_TRACKING_EVENT* tracking_event;
// /** A log message. @since 3.0.0 */
// const LEAP_LOG_EVENT* log_event;
// /** A log messages. @since 4.0.0 */
// const LEAP_LOG_EVENTS* log_events;
// /** A get config value message. @since 3.0.0 */
// const LEAP_CONFIG_RESPONSE_EVENT* config_response_event;
// /** A set config value message. @since 3.0.0 */
// const LEAP_CONFIG_CHANGE_EVENT* config_change_event;
// const LEAP_DROPPED_FRAME_EVENT* dropped_frame_event;
// /** A streaming image message. @since 4.0.0 */
// const LEAP_IMAGE_EVENT* image_event;
// /** A point mapping message. @since 4.0.0 */
// const LEAP_POINT_MAPPING_CHANGE_EVENT* point_mapping_change_event;
// const LEAP_HEAD_POSE_EVENT* head_pose_event;
// };
// } LEAP_CONNECTION_MESSAGE;
@FieldOrder({ "size", "type", "union" })
public static class LEAP_CONNECTION_MESSAGE extends Structure
{
public static class EventUnion extends Union
{
public Pointer pointer;
// Pointer is used for all event types I haven't mapped yet.
public Pointer connection_event;
public Pointer connection_lost_event;
public Pointer device_event;
public Pointer device_status_change_event;
public Pointer policy_event;
public Pointer device_failure_event;
public LEAP_TRACKING_EVENT.ByReference tracking_event;
public Pointer log_event;
public Pointer log_events;
public Pointer config_response_event;
public Pointer config_change_event;
public Pointer dropped_frame_event;
public Pointer image_event;
public Pointer point_mapping_change_event;
public Pointer head_pose_event;
}
public int size;
public short type;
public EventUnion union;
private eLeapEventType typeE;
@Override
public void read()
{
super.read();
// Convert the short in type to an enum constant.
typeE = eLeapEventType.None.getForValue(type);
if (typeE == null)
{
typeE = eLeapEventType.None;
}
switch (typeE)
{
case ConfigChange :
union.setType("config_change_event");
break;
case ConfigResponse :
union.setType("config_response_event");
break;
case Connection :
union.setType("connection_event");
break;
case ConnectionLost :
union.setType("connection_lost_event");
break;
case Device :
union.setType("device_event");
break;
case DeviceFailure :
union.setType("device_failure_event");
break;
case DeviceLost :
union.setType("device_event");
break;
case DeviceStatusChange :
union.setType("device_status_change_event");
break;
case DroppedFrame :
union.setType("dropped_frame_event");
break;
case HeadPose :
union.setType("head_pose_event");
break;
case Image :
union.setType("image_event");
break;
case ImageComplete :
break;
case ImageRequestError :
break;
case LogEvent :
union.setType("log_event");
break;
case LogEvents :
union.setType("log_events");
break;
case None :
union.setType("pointer");
break;
case PointMappingChange :
union.setType("point_mapping_change_event");
break;
case Policy :
union.setType("policy_event");
break;
case Tracking :
union.setType("tracking_event");
break;
default :
System.out.println("Unknown message type: " + typeE);
break;
}
union.read();
}
public static class ByReference extends LEAP_CONNECTION_MESSAGE
implements Structure.ByReference
{
}
}
最后,我当前 LEAP_TRACKING_EVENT
的完整代码
// Original C signature: See the top of this post.
@FieldOrder({ "info", "tracking_frame_id", "nHands", "pHands", "framerate" })
public static class LEAP_TRACKING_EVENT extends Structure
{
public LEAP_FRAME_HEADER info;
public long tracking_frame_id;
public int nHands;
public Pointer pHands;
public float framerate;
// Field to store all LEAP_HAND objects pointed to by pHands.
private LEAP_HAND[] hands;
@Override
public void read()
{
super.read();
// Print frame ID and hand count
System.out.println("======================");
System.out.println("ID: " + tracking_frame_id);
System.out.println("Hands: " + nHands);
if (nHands > 0)
{
// Attempt to read LEAP_HAND data and print info about hand #0.
System.out.println("===");
LEAP_HAND hand = new LEAP_HAND(pHands);
hands = (LEAP_HAND[]) hand.toArray(nHands);
String log = String.format(
"Hand 0| id: %d, type: %d, pos: [%.02f, %.02f, %.02f]%n",
hands[0].id, hands[0].type,
hands[0].palm.position.union.struct.x,
hands[0].palm.position.union.struct.y,
hands[0].palm.position.union.struct.z);
System.out.println(log);
}
System.out.println("======================");
}
public static class ByReference extends LEAP_TRACKING_EVENT
implements Structure.ByReference
{
}
}
new LEAP_HAND(pHands)
只是将指针传递给超级构造函数,然后对其自身调用 read()
(当然,下面链接的要点中仍然提供了此代码)。
编辑 3:无效的内存访问堆栈跟踪
Exception in thread "main" java.lang.Error: Invalid memory access
at com.sun.jna.Native.getInt(Native Method)
at com.sun.jna.Pointer.getInt(Pointer.java:580)
at com.sun.jna.Pointer.getValue(Pointer.java:382)
at com.sun.jna.Structure.readField(Structure.java:732)
at com.sun.jna.Structure.read(Structure.java:591)
at komposten.leapmouse.LeapC$LEAP_HAND.read(LeapC.java:322)
at komposten.leapmouse.LeapC$LEAP_HAND.<init>(LeapC.java:316)
at komposten.leapmouse.LeapC$LEAP_TRACKING_EVENT.read(LeapC.java:236)
at com.sun.jna.Structure.autoRead(Structure.java:2203)
at com.sun.jna.Structure.conditionalAutoRead(Structure.java:561)
at com.sun.jna.Structure.updateStructureByReference(Structure.java:690)
at com.sun.jna.Pointer.getValue(Pointer.java:367)
at com.sun.jna.Structure.readField(Structure.java:732)
at com.sun.jna.Union.readField(Union.java:223)
at com.sun.jna.Structure.read(Structure.java:591)
at komposten.leapmouse.LeapC$LEAP_CONNECTION_MESSAGE.read(LeapC.java:196)
at com.sun.jna.Structure.autoRead(Structure.java:2203)
at com.sun.jna.Function.invoke(Function.java:381)
at com.sun.jna.Library$Handler.invoke(Library.java:265)
at com.sun.jna.Native.invoke(Native.java:1202)
at com.sun.proxy.$Proxy0.LeapPollConnection(Unknown Source)
at komposten.leapmouse.LeapTest.<init>(LeapTest.java:35)
at komposten.leapmouse.LeapTest.main(LeapTest.java:88)
我已经创建了一个包含 LeapTest
和 LeapC
的完整代码的要点,之后根据 SO 上的答案和评论进行了一些更改:LeapJna Gist。
如果需要,我可以在此处内联此代码,但要点有行号并且更容易获得 (IMO) 的概述。
如果需要,这里是我对 LEAP_HAND 的实现(以及完整的 LEAP_TRACKING_EVENT 代码和这两者使用的其他结构映射)。
首先,欢迎来到 Whosebug!其次,感谢您提供更多详细信息。对于您选择的结构映射方式,您遗漏了一些实施细节,但简化实施比添加更多代码更容易。
您在 LEAP_CONNECTION_MESSAGE
的 ByReference
实现的指针构造函数中缺少 super(p)
,因此它可能无法以这种方式正确读取所有值。但是,没有必要使用 ByReference
实现。在函数参数中使用时,JNA 会自动使用结构的指针。删除 ByReference
并用 LEAP_CONNECTION_MESSAGE message = new LEAP_CONNECTION_MESSAGE();
实例化。
另一个问题是试图将本机内存读入结构数组。您对 LEAP_HAND hand = new LEAP_HAND(pHands);
的初始分配可能会为您提供数组的第一个元素,但随后使用 hands = (LEAP_HAND[]) hand.toArray(nHands);
会在本机端分配新内存。您应该改为创建自己的 LEAP_HAND[]
数组,迭代从 0 开始并递增 Native.getNativeSize(LEAP_HAND.class)
的偏移量,并使用 new LEAP_HAND(pHands.share(offset))
实例化每个元素。 (一旦你开始工作,还有其他方法可以稍微更优雅地做到这一点。)
另一个可能的问题点是您将消息类型定义为 public short type
。 API specifies this type as an enum
通常是 和 int
。看看你的要点,你有几个其他的枚举映射,你已经映射了 int
以外的东西(例如,LEAP_HAND
的 type
元素是 byte
),这也可能使这些结构错位。 (不过,根据你的解释和测试,你可能是对的。)
正如我们最终确定的那样,还有一些其他问题需要解决,包括结构对齐和字节顺序。 Pointer.dump()
作为win的调试工具!
经过与@DanielWiddis 的大量讨论(谢谢!),我们终于找到了问题所在。按照 Daniel 的建议,使用 Pointer.dump()
,我手动将内存映射到 JNA 加载到 LEAP_TRACKING_EVENT
class:
中的数据
memory | mapped to
-----------|--------------
[bceba2ed] | info.reserved
[f5010000] | info.reserved
[d7990600] | info.frame_id
[00000000] | info.frame_id
[adbaca0b] | info.timestamp
[04000000] | info.timestamp
[9d990600] | tracking_frame_id
[00000000] | tracking_frame_id
[01000000] | nHands
[80e7a2ed] |
[f5010000] | pHands
[6c4edd42] | pHands
[00000000] | framerate
由于 JNA 在 nHands
和 pHands
之间跳过 4 个字节的结构的默认对齐设置,导致 pHands
指向 0x42dd4e6c000001f5
而不是 0x000001f5eda2e780
(在这个例子中)。因此 pHands
指向了完全错误的位置。
为了解决这个问题,我只是将 LEAP_TRACKING_DATA
更改为使用 ALIGN_NONE
instead of ALIGN_DEFAULT
。
我正在尝试使用 JNA 在 Leap Motion's C API (LeapC) 和 Java 之间架起桥梁,因为 Leap Motion 已经弃用了他们的官方 Java 绑定。但是,我对 C 不熟悉,以前也没有使用过 JNA。到目前为止,我已经设法让一些事情开始工作(比如连接到我的 Leap Motion 控制器并轮询它的状态事件),但在接收实际跟踪数据时我遇到了困难。
LeapC 的相关结构在 LeapC.h 中定义如下:
/** \ingroup Structs
* A snapshot, or frame of data, containing the tracking data for a single moment in time.
* The LEAP_FRAME struct is the container for all the tracking data.
* @since 3.0.0
*/
typedef struct _LEAP_TRACKING_EVENT {
LEAP_FRAME_HEADER info;
int64_t tracking_frame_id;
/** The number of hands tracked in this frame, i.e. the number of elements in
* the pHands array.
*/
uint32_t nHands;
/**
* A pointer to the array of hands tracked in this frame.
*/
LEAP_HAND* pHands;
/**
* Current tracking frame rate in hertz.
*/
float framerate;
} LEAP_TRACKING_EVENT;
我在 JNA 中实现它的尝试只取得了部分成功:info
、tracking_frame_id
和 nHands
字段被正确读取并包含预期信息。 pHands
拒绝工作。
尝试 1
首先,我尝试将 pHands
视为指向 LEAP_HAND 数组开头的指针:
@FieldOrder({ "info", "tracking_frame_id", "nHands", "pHands", "framerate" })
public static class LEAP_TRACKING_EVENT extends Structure
{
public LEAP_FRAME_HEADER info;
public long tracking_frame_id;
public int nHands;
public Pointer pHands;
public float framerate;
}
尝试从指针指向的地址读取任何内容,无论是完整的结构(new LEAP_HAND(pHands)
)还是只是一个字节(pHands.getByte()
)总是会从 JNA 得到无效的内存访问错误.
根据阅读各种其他 Whosebug 问题和答案,我似乎应该这样做。
尝试 2
其次,我尝试将 pHands
视为直立数组:
@FieldOrder({ "info", "tracking_frame_id", "nHands", "pHands", "framerate" })
public static class LEAP_TRACKING_EVENT extends Structure
{
public LEAP_FRAME_HEADER info;
public long tracking_frame_id;
public int nHands;
public LEAP_HAND[] pHands = new LEAP_HAND[1]; //Size-limited to 1 here while testing.
public float framerate;
}
假设只存在一只手(如上面的 JNA 代码中所定义),数组由 LEAP_HAND 的实例填充,但它包含垃圾数据(主要是零)。奇怪的是,当我沿着 x 轴移动手时,pHands[0].palm.position.z
的值确实发生了变化,因此它似乎与此处的正确内存部分部分重叠,但未正确对齐。
我一定是在读取这个数组时做错了什么。有人知道我错过了什么吗?
编辑:来自 SDK 示例之一的工作 C 代码
他们这样称呼 LeapPollConnection
:result = LeapPollConnection(connectionHandle, timeout, &msg);
。如果 msg
包含跟踪事件,他们就会调用 handleTrackingEvent(msg.tracking_event);
。该函数定义如下:
//File: ExampleConnection.c
static void handleTrackingEvent(const LEAP_TRACKING_EVENT *tracking_event){
if(ConnectionCallbacks.on_frame){
ConnectionCallbacks.on_frame(tracking_event);
}
}
ConnectionCallbacks.on_frame
绑定到以下 OnFrame
函数:
//File: CallbackSample.c
/** Callback for when a frame of tracking data is available. */
static void OnFrame(const LEAP_TRACKING_EVENT *frame){
printf("Frame %lli with %i hands.\n", (long long int)frame->info.frame_id, frame->nHands);
for(uint32_t h = 0; h < frame->nHands; h++){
LEAP_HAND* hand = &frame->pHands[h];
printf(" Hand id %i is a %s hand with position (%f, %f, %f).\n",
hand->id,
(hand->type == eLeapHandType_Left ? "left" : "right"),
hand->palm.position.x,
hand->palm.position.y,
hand->palm.position.z);
}
}
编辑 2:目前所有(相关)代码:
首先是轮询和获取事件的代码。
// Define an object to receive an event message into.
LEAP_CONNECTION_MESSAGE.ByReference messageRef = new LEAP_CONNECTION_MESSAGE.ByReference();
while (true)
{
// Poll LeapC for an event. A status code for success is returned,
// and messageRef is populated with the event message.
LeapC.INSTANCE.LeapPollConnection(leapConnection.getValue(), 500, messageRef);
// If the event is a tracking event, get the event data.
if (messageRef.type == eLeapEventType.Tracking.getShortValue())
{
LEAP_TRACKING_EVENT event = messageRef.union.tracking_event;
}
// Sleep a moment before polling again.
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
}
}
下面是 PollLeapConnection
和 LEAP_CONNECTION_MESSAGE
的实现。
LeapPollConnection (LeapC API ref)
// Original C signature:
// LeapPollConnection(LEAP_CONNECTION hConnection, uint32_t timeout, LEAP_CONNECTION_MESSAGE* evt);
public eLeapRS LeapPollConnection(Pointer hConnection, int timeout,
LEAP_CONNECTION_MESSAGE.ByReference message);
LEAP_CONNECTION_MESSAGE (LeapC API ref)
// Original C signature:
// typedef struct _LEAP_CONNECTION_MESSAGE {
// /**
// * The size of this message struct. @since 3.0.0
// */
// uint32_t size;
//
// /**
// * The message type. @since 3.0.0
// */
// eLeapEventType type;
//
// /**
// * A pointer to the event data for the current type of message. @since 3.0.0
// */
// union {
// /** An untyped pointer. @since 3.0.0 */
// const void* pointer;
// /** A connection message. @since 3.0.0 */
// const LEAP_CONNECTION_EVENT* connection_event;
// /** A connection lost. @since 3.0.0 */
// const LEAP_CONNECTION_LOST_EVENT* connection_lost_event;
// /** A device detected message. @since 3.0.0 */
// const LEAP_DEVICE_EVENT* device_event;
// /** A device's status has changed. @since 3.1.3 */
// const LEAP_DEVICE_STATUS_CHANGE_EVENT* device_status_change_event;
// /** A policy message. @since 3.0.0 */
// const LEAP_POLICY_EVENT* policy_event;
// /** A device failure message. @since 3.0.0 */
// const LEAP_DEVICE_FAILURE_EVENT* device_failure_event;
// /** A tracking message. @since 3.0.0 */
// const LEAP_TRACKING_EVENT* tracking_event;
// /** A log message. @since 3.0.0 */
// const LEAP_LOG_EVENT* log_event;
// /** A log messages. @since 4.0.0 */
// const LEAP_LOG_EVENTS* log_events;
// /** A get config value message. @since 3.0.0 */
// const LEAP_CONFIG_RESPONSE_EVENT* config_response_event;
// /** A set config value message. @since 3.0.0 */
// const LEAP_CONFIG_CHANGE_EVENT* config_change_event;
// const LEAP_DROPPED_FRAME_EVENT* dropped_frame_event;
// /** A streaming image message. @since 4.0.0 */
// const LEAP_IMAGE_EVENT* image_event;
// /** A point mapping message. @since 4.0.0 */
// const LEAP_POINT_MAPPING_CHANGE_EVENT* point_mapping_change_event;
// const LEAP_HEAD_POSE_EVENT* head_pose_event;
// };
// } LEAP_CONNECTION_MESSAGE;
@FieldOrder({ "size", "type", "union" })
public static class LEAP_CONNECTION_MESSAGE extends Structure
{
public static class EventUnion extends Union
{
public Pointer pointer;
// Pointer is used for all event types I haven't mapped yet.
public Pointer connection_event;
public Pointer connection_lost_event;
public Pointer device_event;
public Pointer device_status_change_event;
public Pointer policy_event;
public Pointer device_failure_event;
public LEAP_TRACKING_EVENT.ByReference tracking_event;
public Pointer log_event;
public Pointer log_events;
public Pointer config_response_event;
public Pointer config_change_event;
public Pointer dropped_frame_event;
public Pointer image_event;
public Pointer point_mapping_change_event;
public Pointer head_pose_event;
}
public int size;
public short type;
public EventUnion union;
private eLeapEventType typeE;
@Override
public void read()
{
super.read();
// Convert the short in type to an enum constant.
typeE = eLeapEventType.None.getForValue(type);
if (typeE == null)
{
typeE = eLeapEventType.None;
}
switch (typeE)
{
case ConfigChange :
union.setType("config_change_event");
break;
case ConfigResponse :
union.setType("config_response_event");
break;
case Connection :
union.setType("connection_event");
break;
case ConnectionLost :
union.setType("connection_lost_event");
break;
case Device :
union.setType("device_event");
break;
case DeviceFailure :
union.setType("device_failure_event");
break;
case DeviceLost :
union.setType("device_event");
break;
case DeviceStatusChange :
union.setType("device_status_change_event");
break;
case DroppedFrame :
union.setType("dropped_frame_event");
break;
case HeadPose :
union.setType("head_pose_event");
break;
case Image :
union.setType("image_event");
break;
case ImageComplete :
break;
case ImageRequestError :
break;
case LogEvent :
union.setType("log_event");
break;
case LogEvents :
union.setType("log_events");
break;
case None :
union.setType("pointer");
break;
case PointMappingChange :
union.setType("point_mapping_change_event");
break;
case Policy :
union.setType("policy_event");
break;
case Tracking :
union.setType("tracking_event");
break;
default :
System.out.println("Unknown message type: " + typeE);
break;
}
union.read();
}
public static class ByReference extends LEAP_CONNECTION_MESSAGE
implements Structure.ByReference
{
}
}
最后,我当前 LEAP_TRACKING_EVENT
的完整代码
// Original C signature: See the top of this post.
@FieldOrder({ "info", "tracking_frame_id", "nHands", "pHands", "framerate" })
public static class LEAP_TRACKING_EVENT extends Structure
{
public LEAP_FRAME_HEADER info;
public long tracking_frame_id;
public int nHands;
public Pointer pHands;
public float framerate;
// Field to store all LEAP_HAND objects pointed to by pHands.
private LEAP_HAND[] hands;
@Override
public void read()
{
super.read();
// Print frame ID and hand count
System.out.println("======================");
System.out.println("ID: " + tracking_frame_id);
System.out.println("Hands: " + nHands);
if (nHands > 0)
{
// Attempt to read LEAP_HAND data and print info about hand #0.
System.out.println("===");
LEAP_HAND hand = new LEAP_HAND(pHands);
hands = (LEAP_HAND[]) hand.toArray(nHands);
String log = String.format(
"Hand 0| id: %d, type: %d, pos: [%.02f, %.02f, %.02f]%n",
hands[0].id, hands[0].type,
hands[0].palm.position.union.struct.x,
hands[0].palm.position.union.struct.y,
hands[0].palm.position.union.struct.z);
System.out.println(log);
}
System.out.println("======================");
}
public static class ByReference extends LEAP_TRACKING_EVENT
implements Structure.ByReference
{
}
}
new LEAP_HAND(pHands)
只是将指针传递给超级构造函数,然后对其自身调用 read()
(当然,下面链接的要点中仍然提供了此代码)。
编辑 3:无效的内存访问堆栈跟踪
Exception in thread "main" java.lang.Error: Invalid memory access
at com.sun.jna.Native.getInt(Native Method)
at com.sun.jna.Pointer.getInt(Pointer.java:580)
at com.sun.jna.Pointer.getValue(Pointer.java:382)
at com.sun.jna.Structure.readField(Structure.java:732)
at com.sun.jna.Structure.read(Structure.java:591)
at komposten.leapmouse.LeapC$LEAP_HAND.read(LeapC.java:322)
at komposten.leapmouse.LeapC$LEAP_HAND.<init>(LeapC.java:316)
at komposten.leapmouse.LeapC$LEAP_TRACKING_EVENT.read(LeapC.java:236)
at com.sun.jna.Structure.autoRead(Structure.java:2203)
at com.sun.jna.Structure.conditionalAutoRead(Structure.java:561)
at com.sun.jna.Structure.updateStructureByReference(Structure.java:690)
at com.sun.jna.Pointer.getValue(Pointer.java:367)
at com.sun.jna.Structure.readField(Structure.java:732)
at com.sun.jna.Union.readField(Union.java:223)
at com.sun.jna.Structure.read(Structure.java:591)
at komposten.leapmouse.LeapC$LEAP_CONNECTION_MESSAGE.read(LeapC.java:196)
at com.sun.jna.Structure.autoRead(Structure.java:2203)
at com.sun.jna.Function.invoke(Function.java:381)
at com.sun.jna.Library$Handler.invoke(Library.java:265)
at com.sun.jna.Native.invoke(Native.java:1202)
at com.sun.proxy.$Proxy0.LeapPollConnection(Unknown Source)
at komposten.leapmouse.LeapTest.<init>(LeapTest.java:35)
at komposten.leapmouse.LeapTest.main(LeapTest.java:88)
我已经创建了一个包含 LeapTest
和 LeapC
的完整代码的要点,之后根据 SO 上的答案和评论进行了一些更改:LeapJna Gist。
如果需要,我可以在此处内联此代码,但要点有行号并且更容易获得 (IMO) 的概述。
如果需要,这里是我对 LEAP_HAND 的实现(以及完整的 LEAP_TRACKING_EVENT 代码和这两者使用的其他结构映射)。
首先,欢迎来到 Whosebug!其次,感谢您提供更多详细信息。对于您选择的结构映射方式,您遗漏了一些实施细节,但简化实施比添加更多代码更容易。
您在 LEAP_CONNECTION_MESSAGE
的 ByReference
实现的指针构造函数中缺少 super(p)
,因此它可能无法以这种方式正确读取所有值。但是,没有必要使用 ByReference
实现。在函数参数中使用时,JNA 会自动使用结构的指针。删除 ByReference
并用 LEAP_CONNECTION_MESSAGE message = new LEAP_CONNECTION_MESSAGE();
实例化。
另一个问题是试图将本机内存读入结构数组。您对 LEAP_HAND hand = new LEAP_HAND(pHands);
的初始分配可能会为您提供数组的第一个元素,但随后使用 hands = (LEAP_HAND[]) hand.toArray(nHands);
会在本机端分配新内存。您应该改为创建自己的 LEAP_HAND[]
数组,迭代从 0 开始并递增 Native.getNativeSize(LEAP_HAND.class)
的偏移量,并使用 new LEAP_HAND(pHands.share(offset))
实例化每个元素。 (一旦你开始工作,还有其他方法可以稍微更优雅地做到这一点。)
另一个可能的问题点是您将消息类型定义为 public short type
。 API specifies this type as an enum
通常是 和 int
。看看你的要点,你有几个其他的枚举映射,你已经映射了 int
以外的东西(例如,LEAP_HAND
的 type
元素是 byte
),这也可能使这些结构错位。 (不过,根据你的解释和测试,你可能是对的。)
正如我们最终确定的那样,还有一些其他问题需要解决,包括结构对齐和字节顺序。 Pointer.dump()
作为win的调试工具!
经过与@DanielWiddis 的大量讨论(谢谢!),我们终于找到了问题所在。按照 Daniel 的建议,使用 Pointer.dump()
,我手动将内存映射到 JNA 加载到 LEAP_TRACKING_EVENT
class:
memory | mapped to
-----------|--------------
[bceba2ed] | info.reserved
[f5010000] | info.reserved
[d7990600] | info.frame_id
[00000000] | info.frame_id
[adbaca0b] | info.timestamp
[04000000] | info.timestamp
[9d990600] | tracking_frame_id
[00000000] | tracking_frame_id
[01000000] | nHands
[80e7a2ed] |
[f5010000] | pHands
[6c4edd42] | pHands
[00000000] | framerate
由于 JNA 在 nHands
和 pHands
之间跳过 4 个字节的结构的默认对齐设置,导致 pHands
指向 0x42dd4e6c000001f5
而不是 0x000001f5eda2e780
(在这个例子中)。因此 pHands
指向了完全错误的位置。
为了解决这个问题,我只是将 LEAP_TRACKING_DATA
更改为使用 ALIGN_NONE
instead of ALIGN_DEFAULT
。