如何通过JNA映射到数组的指针?我不断收到无效的内存访问或垃圾数据

问题描述 投票:2回答:1

由于Leap Motion已弃用了它们的正式Java绑定,因此我试图使用JNA在Leap Motion's C API (LeapC)和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中实现它的尝试仅部分成功:infotracking_frame_idnHands字段被正确读取并包含预期的信息。 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发出无效的内存访问错误。

基于阅读各种其他StackOverflow问题和答案,看来我应该做这样的事情。

尝试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代码

他们这样称呼LeapPollConnectionresult = 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)
    {
    }
}

这是PollLeapConnectionLEAP_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的完整代码>

LeapC API ref

// 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()(当然,此代码仍在下面链接的要点中可用)。


[如果需要,这是我对LEAP_HAND的实现(以及这两个人使用的完整LEAP_TRACKING_EVENT代码和其他结构映射)。

我正在尝试使用JNA在Leap Motion的C API(LeapC)和Java之间架起桥梁,因为Leap Motion已弃用了它们的官方Java绑定。但是,我不熟悉C,并且以前没有使用过JNA。...

java arrays pointers jna leap-motion
1个回答
0
投票

首先,欢迎使用StackOverflow!其次,感谢您提供其他详细信息。您缺少一些选择的映射结构的实现细节,但是简化实现比添加更多代码要容易。

super(p)实现的ByReference的指针构造函数中缺少LEAP_CONNECTION_MESSAGE,因此它可能无法以这种方式正确读取所有值。但是,没有必要使用ByReference实现。当在函数参数中使用JNA时,它会自动使用Structure的指针。删除ByReference,仅用LEAP_CONNECTION_MESSAGE message = new LEAP_CONNECTION_MESSAGE();实例化。

© www.soinside.com 2019 - 2024. All rights reserved.