我应该如何从结构中提取复杂的信息?

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

我在 C++ 中有一个复杂的结构。现在我需要通过 JNA 获取该值。我尝试了一些方法,但都失败了。我不确定问题出在哪里。

这是我的Java结构,它是由JNACreator生成的(https://code.google.com/archive/p/jnaerator/):

typedef struct SqWholeImageInfo 
{
    char *fileName;
    SqPicHead *phead;
    SqPersonInfo *personInfo;
    SqExtraInfo *extra;
    SqImageInfo **macrograph;
    SqImageInfo *thumbnail;
    SqPicInfo **sliceLayerInfo;
    SqSliceInfo **sliceInfo;
}SqWholeImageInfo;
package com.hys.mapper;

import com.sun.jna.Pointer;
import com.sun.jna.Structure;

import java.util.Arrays;
import java.util.List;

public class SqWholeImageInfo extends Structure {
    public Pointer fileName;
    public com.hys.mapper.SqPicHead.ByReference phead;
    public com.hys.mapper.SqPersonInfo.ByReference personInfo;
    public com.hys.mapper.SqExtraInfo.ByReference extra;
    public com.hys.mapper.SqImageInfo.ByReference[] macrograph;
    public com.hys.mapper.SqImageInfo.ByReference thumbnail;
    public com.hys.mapper.SqPicInfo.ByReference[] sliceLayerInfo;
    public com.hys.mapper.SqSliceInfo.ByReference[] sliceInfo;
    public SqSdpcInfo() {
        super();
    }
    protected List<String> getFieldOrder() {
        return Arrays.asList("fileName", "phead", "personInfo", "extra", "macrograph", "thumbnail", "sliceLayerInfo", "sliceInfo");
    }
    public SqWholeImageInfo(Pointer fileName, com.hys.mapper.SqPicHead.ByReference phead, com.hys.mapper.SqPersonInfo.ByReference personInfo, com.hys.mapper.SqExtraInfo.ByReference extra, com.hys.mapper.SqImageInfo.ByReference macrograph[], com.hys.mapper.SqImageInfo.ByReference thumbnail, com.hys.mapper.SqPicInfo.ByReference sliceLayerInfo[], com.hys.mapper.SqSliceInfo.ByReference sliceInfo[]) {
        super();
        this.fileName = fileName;
        this.phead = phead;
        this.personInfo = personInfo;
        this.extra = extra;
        if ((macrograph.length != this.macrograph.length))
            throw new IllegalArgumentException("Wrong array size !");
        this.macrograph = macrograph;
        this.thumbnail = thumbnail;
        if ((sliceLayerInfo.length != this.sliceLayerInfo.length))
            throw new IllegalArgumentException("Wrong array size !");
        this.sliceLayerInfo = sliceLayerInfo;
        if ((sliceInfo.length != this.sliceInfo.length))
            throw new IllegalArgumentException("Wrong array size !");
        this.sliceInfo = sliceInfo;
    }
    public static class ByReference extends SqWholeImageInfo implements Structure.ByReference {

    };
    public static class ByValue extends SqWholeImageInfo implements Structure.ByValue {

    };
}

界面为:

// in c
DECLSPEC GLOBAL(int) openfile(char *sdpcPath, SqWholeImageInfo **info);

// call in c
char *filePath = "path/to/file";
SqWholeImageInfo *si = NULL;
int ret = openfile(filePath, &si);
// that's why it needs a pointer to structure
for (int i = 0; i < si->picHead->hierarchy; i++)
{
    PrintPicInfo(si->sliceLayerInfo[i]); 
}
// in java
public int openfile(String sdpcPath, SqWholeImageInfo.ByReference info);

如您所见,该结构体中有结构体数组字段。我首先尝试直接调用它:

String filePath = "path/to/file";
SqWholeImageInfo.ByReference info = new SqWholeImageInfo.ByReference();
int status = SdpcService.INSTANCE.openfile(filePath, info);
System.out.println("file:" + info.fileName.getString(0));

然后我得到一个错误:

数组字段必须初始化

我首先猜测它需要 SqWholeImageInfo 数组。我将界面更改为

// interface
public int openfile(String sdpcPath, SqWholeImageInfo.ByReference[] info);
// call
String filePath = "path/to/file";
SqWholeImageInfo.ByReference[] info = new SqWholeImageInfo.ByReference[1];
info[0] = new SqWholeImageInfo.ByReference();
int status = SdpcService.INSTANCE.openfile(filePath, info);
System.out.println("file:" + info[0].fileName.getString(0));

然后我得到了同样的错误,我猜这是由SqWholeImageInfo中的结构体数组引起的。所以我将

com.hys.mapper.SqImageInfo.ByReference[]
的类型更改为
PointerByReference
,如下所示:

...
    public Pointer fileName;
    public com.hys.mapper.SqPicHead.ByReference phead;
    public com.hys.mapper.SqPersonInfo.ByReference personInfo;
    public com.hys.mapper.SqExtraInfo.ByReference extra;
    public PointerByReference macrograph;
    public com.hys.mapper.SqImageInfo.ByReference thumbnail;
    public PointerByReference sliceLayerInfo;
    public PointerByReference sliceInfo; // now here is no more array.
...

我在状态中得到了返回值

0
(成功!),但是
filename
的值错误。然后我发现了这个:

// got file:native@0x1f4b1b0b050
// System.out.println("file:" + info.fileName);
// got error value it should be 'path/to/file'
System.out.println("file:" + info.fileName.getString(0));
// And another error: Cannot read field "srcWidth" because "info.phead" is null this can be fixed by using structure array of info
System.out.println("width:" + info.phead.srcWidth);
System.out.println("height:" + info.phead.srcHeight);

我不知道为什么。指针类型不能作为结构体数组的类型吗?如果有效,我应该如何将指针转换为结构体?

还是我操作有问题?

或者有什么方法可以初始化一个可变长度的数组吗?

我检查了其他问题:


我尝试了一些简单的例子,似乎我对结构嵌套有一些误解,但我仍然不知道问题出在哪里。 这是C++和Java的全部代码

//header file
// framework.h
#pragma once
#define WIN32_LEAN_AND_MEAN    
#include <windows.h>
#define DECLSPEC extern "C" _declspec(dllexport)
typedef struct StructA
{
  int x;
  int y;
  char* str1;
  unsigned char* seq1[128];
}StructA;

typedef struct StructB
{
  float a;
  double b;
  char** str_arr;
  int* ps;
}StructB;

typedef struct StructC
{
  StructA* sa;
  StructB** sbs;
}StructC;

DECLSPEC int TestFunc1(StructA* sa);
DECLSPEC int TestFunc2(StructA* sa, StructC** scs);

// source file
// dllmain.cpp
#include "pch.h"

#include <stdio.h>
#include "malloc.h"
#include "string.h"
using namespace std;

int TestFunc1(StructA* sa)
{
  sa->x = 10;
  sa->y = 12;
  sa->str1 = (char*)malloc(sizeof(char) * 6);
  memset(sa->str1, 0, sizeof(char) * 6);
  strcpy(sa->str1, "hello");

  unsigned char testSeq[] = "abc";
  sa->seq1[0] = testSeq;

  return 0;
}

int TestFunc2(StructA* sa, StructC** scs) {
  sa->x = 101;
  sa->y = 121;
  sa->str1 = (char*)malloc(sizeof(char) * 6);
  memset(sa->str1, 0, sizeof(char) * 6);
  strcpy(sa->str1, "hello");

  unsigned char testSeq[] = "abc";
  sa->seq1[0] = testSeq;

  if (*scs == NULL) {
    printf("init sc\n");
    *scs = (StructC*)malloc(sizeof(StructC));
    (*scs)->sa = (StructA*)malloc(sizeof(StructA));
    (*scs)->sa->x = 0;  // Initialize x to some default value
    (*scs)->sa->y = 0;  // Initialize y to some default value
    (*scs)->sa->str1 = (char*)malloc(sizeof(char) * 10);  // Allocate memory for str1
    memset((*scs)->sa->str1, 0, sizeof(char) * 10);
    strcpy((*scs)->sa->str1, "");  // Initialize str1 to an empty string
    (*scs)->sbs = (StructB**)malloc(sizeof(StructB*) * 4);
    for (int i = 0; i < 4; ++i) {
      (*scs)->sbs[i] = (StructB*)malloc(sizeof(StructB));
      (*scs)->sbs[i]->a = 0.0;  // Initialize a to some default value
      (*scs)->sbs[i]->b = 0.0;  // Initialize b to some default value
      (*scs)->sbs[i]->str_arr = NULL;  // Initialize str_arr to NULL
      (*scs)->sbs[i]->ps = NULL;  // Initialize ps to NULL
    }

    printf("After malloc - sa: %p, x: %d, y: %d, str1: %p\n",
      (*scs)->sa, (*scs)->sa->x, (*scs)->sa->y, (*scs)->sa->str1);
  }
  else {
    printf("no init sc\n");
  }

  // Set values for StructA members
  (*scs)->sa->x = 110;
  (*scs)->sa->y = 120;
  (*scs)->sa->str1 = (char*)malloc(sizeof(char) * 10);
  memset((*scs)->sa->str1, 0, sizeof(char) * 10);
  strcpy((*scs)->sa->str1, "hello119");

  printf("After setting values - sa: %p, x: %d, y: %d, str1: %p\n",
    (*scs)->sa, (*scs)->sa->x, (*scs)->sa->y, (*scs)->sa->str1);

  // Set values for StructB members
  (*scs)->sbs[0]->a = 1.5;
  (*scs)->sbs[0]->b = 1.8;
  int ia[] = { 1, 2, 3 };
  (*scs)->sbs[0]->ps = ia;

  return 0;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

Java 代码

package com.hys;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.ptr.PointerByReference;

import java.util.Arrays;
import java.util.List;

public class TestdllService {
    public interface MyLibrary extends Library {
        MyLibrary INSTANCE = Native.load("D:\\WS_VS\\testdll\\x64\\Release\\testdll.dll", MyLibrary.class);

        int TestFunc1(StructA sa);
        int TestFunc2(StructA sa, StructC.ByReference scs);
    }

    public static class StructA extends Structure {
        public int x;
        public int y;
        public String str1;
        public Pointer[] seq1 = new Pointer[128];

        // Specify the order of fields explicitly
        protected List<String> getFieldOrder() {
            return Arrays.asList("x", "y", "str1", "seq1");
        }

        public StructA() {
            super();
            // Initialize fields to default values
            x = 0;
            y = 0;
            str1 = "";
            Arrays.fill(seq1, Pointer.NULL);
        }

        public static class ByReference extends StructA implements Structure.ByReference {
        }

        public static class ByValue extends StructA implements Structure.ByValue {
        }
    }

    public static class StructB extends Structure {
        public float a;
        public double b;
        public Pointer str_arr; // Assuming this is equivalent to char**
        public Pointer ps;

        public static class ByReference extends StructB implements Structure.ByReference {
            protected List<String> getFieldOrder() {
                return Arrays.asList("a", "b", "str_arr", "ps");
            }
        }

        public static class ByValue extends StructB implements Structure.ByValue {
        }
    }

    public static class StructC extends Structure {
        public StructA.ByReference sa;
        public StructB.ByReference sbs;

        // Specify the order of fields explicitly
        protected List<String> getFieldOrder() {
            return Arrays.asList("sa", "sbs");
        }

        public StructC() {
            super();
        }

        public StructC(Pointer p) {
            super(p);
            read();
        }

        public void initSbsArray(int size) {
            sbs = new StructB.ByReference();
            sbs.toArray(size);
        }

        public static class ByReference extends StructC implements Structure.ByReference {
            public ByReference() {
                super(Pointer.NULL);
            }

            public ByReference(Pointer p) {
                super(p);
            }
        }

        public static class ByValue extends StructC implements Structure.ByValue {
        }
    }

    public static void main(String[] args) {
        MyLibrary myLibrary = MyLibrary.INSTANCE;

        StructA sa = new StructA();
//        int ret = myLibrary.TestFunc1(sa);
        StructC.ByReference scs = new StructC.ByReference(Pointer.NULL);
        int dynamicSize = 4;
        scs.initSbsArray(dynamicSize);

        int ret = myLibrary.TestFunc2(sa, scs);
        System.out.println("Result: " + ret);
        // Access the modified struct or handle the result as needed
        if (scs != null && scs.getPointer() != null) {
            // Read the values from the modified struct
            scs.read(); // This populates the Java fields

            // Access the values
            System.out.println("Modified sa.x: " + scs.sa.x);
            System.out.println("Modified sa.y: " + scs.sa.y);
            System.out.println("Modified sa.str1: " + scs.sa.str1);
        } else {
            System.out.println("Pointer is null, no modification occurred.");
        }


    }
}

TestFunc1
效果很好。但是
TestFunc2
的值是错误的。

// output of TestFunc2
Result: 0
Modified sa.x: -1586624800 // it should be 110
Modified sa.y: 483 // it should be 120
Modified sa.str1: P`r��  // it should be hello119
init sc
After malloc - sa: 000001E3A16E06E0, x: 0, y: 0, str1: 000001E3A171EEC0
After setting values - sa: 000001E3A16E06E0, x: 110, y: 120, str1: 000001E3A171EE60
java structure jna
1个回答
0
投票

引用您的评论

从 API 来看,JNACreator 给出了正确的结果。

SqImageInfo**
SqPicInfo**
SqSliceInfo**
都是
SqWholeImageInfo
中的结构体指针数组。长度取决于
int SqPicHead.hierarchy
,所以我需要先获取SqPicHead *phead中的信息,然后才能获取SqPicInfo **sliceLayerInfo的长度。

这是用于映射的关键信息:

  1. 数组映射正确,但仍需要初始化。
  2. 你不知道什么时候初始化它需要什么大小的映射,所以你可以在开始时用大小1来初始化它们。
  3. 如果您调用 Native 并期望它填充此数组,则可以覆盖
    read()
    以获取适当的大小并在读取完整结构之前重新分配数组大小。

我没有

SqPicHead
的完整细节,但鉴于您的信息,这样的结构可能会起作用(或者至少为您指出正确的方向):

@FieldOrder ({"fileName", "phead", "personInfo", "extra", "macrograph", "thumbnail", "sliceLayerInfo", "sliceInfo"})
public class SqWholeImageInfo extends Structure {
    public String fileName;
    public SqPicHead.ByReference phead;
    public SqPersonInfo.ByReference personInfo;
    public SqExtraInfo.ByReference extra;
    public SqImageInfo.ByReference[] macrograph = new SqImageInfo.ByReference[1];
    public SqImageInfo.ByReference thumbnail;
    public SqPicInfo.ByReference[] sliceLayerInfo = new SqPicInfo.ByReference[1];
    public SqSliceInfo.ByReference[] sliceInfo = new SqSliceInfo.ByReference[1];
    public SqSdpcInfo() {
        super();
    }

    @Override
    public void read() {
        readField("phead");
        macrograph = new SqImageInfo.ByReference[phead.hierarchy];
        sliceLayerInfo = new SqPicInfo.ByReference[phead.hierarchy];
        sliceInfo = new SqSliceInfo.ByReference[phead.hierarchy];
        super.read();
    }

    public static class ByReference extends SqWholeImageInfo implements Structure.ByReference {};
}

我做了一些其他更改:

  • @FieldOrder
    注释更加清晰,并且与您的方法覆盖执行相同的操作
  • 我将
    char *
    映射更改为
    String
    ,这是更好的 JNA 映射
  • 我省略了带参数的构造函数,因为我认为你不需要它或不想要它
  • 我省略了结构的
    ByValue
    版本,因为你不需要它
© www.soinside.com 2019 - 2024. All rights reserved.