无法通过jna将带有单字节数组字段的结构传递给本机代码

ukxgm1gy  于 2021-08-20  发布在  Java
关注(0)|答案(2)|浏览(379)

我成功地将单字节数组字段作为另一个更大结构的一部分输出,从而获得此结构。但是,当我将此结构作为输入参数传递时,本机代码无法破译字节数组中的数据。我已经验证了本机代码能够在没有jna的情况下成功处理直接调用调用。我需要知道,java中的结构定义有什么问题可能导致这个问题。
详细信息我正在使用第三方库,该库的guid具有以下c结构:

typedef struct GuidType
{
    uint8_t data[16];
}  GuidType;

guidtype用于其他更大的结构中,是用于执行各种数据操作的常用标识信息。例如

typedef struct DataObjectType1 {
   GuidType guid;
   const char * detail1;
   // various other fields
} DataObjectType1;

typedef struct DataObjectType1Array {
   size_t size;
   DataObjectType1**data; // array of pointers. Each pointer points to a structure
} 

typedef struct DataObjectType2 {
   GuidType guid;
   const char * detail2;
   // various other fields
} DataObjectType2;

第三方库具有以下行的API:

int getAllDataObjectType1Instances(DataObjectType1Array**dataObjectType1Instances);
int getDataObjectType2ForGuid(GuidType guid, DataObjectType2**dataObjectType2);

在阅读了jna文档并使用JNAborator之后,我可以得出以下java jna代码:

GuidType extends Structure {
  public byte[] data = new byte[16];

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

  public GuidType() { }
}

DataObjectType1 extends Structure {
  public GuidType guid;
  public String detail1;
  // various other fields

  // Default constructor and constructor with pointer as argument
}

DataObjectType1Array extends Structure {
   public int size;
   public Pointer data;

  // Default constructor and constructor with pointer as argument
}

DataObjectType2 extends Structure {
  public GuidType guid;
  public String detail2;
  // various other fields

  // Default constructor and constructor with pointer as argument
}

// Native API equivalent in Java using JNA 
int getAllDataObjectType1Instances(PointerByReference dataObjectType1Instances);
int getDataObjectType2ForGuid(GuidType guid, PointerByReference dataObjectType2Instance);

通过以上代码,我能够成功调用getalldataobjecttype1instances api,该api返回所有dataobjecttype1示例,并在结构中正确初始化所有字段。但是,getdataobjecttype2forguid调用始终失败。代码位于以下行中:

PointerByReference dataObjectType1Instances = new PointerByReference();
int result = libraryInstance.getAllDataObjectType1Instances(dataObjectType1Instances);
if(result == 0){
    DataObjectType1Array dataObjectType1Array = new DataObjectType1Array(dataObjectType1Instances.getValue());
    Pointer[] pointers = dataObjectType1Array.data.getPointerArray(0, dataObjectType1Array.size);
    for(Pointer pointer : pointers){
       DataObjectType1 dataObjectType1 = new DataObjectType1(pointer);
       System.out.println(Arrays.toString(dataObjectType1.guid.data)); // this shows the correct GUID details

      // More print statements that indicate other fields in dataObjectType1 are also initialized correctly.

       PointerByReference dataObjectType2PointerByReference = new PointerByReference(); 

       result = libraryInstance.getDataObjectType2ForGuid(dataObjectType1.guid, dataObjectType2PointerByReference); // this always fails. On success, library assigns the address of a DataObjectType2 pointer to dataObjectType2PointerByReference.

      // ... Code to extract DataObjectType2 instance from dataObjectType2Reference.
    }
}

如上所述,libraryinstance.getdataobjecttype2forguid()调用失败,并出现一个错误,表明实际数据没有传递到本机代码。我还尝试在字节数组字段中使用正确的数据创建一个新的guidtype示例,然后将其传递给getdataobjecttype2forguid(),但没有成功。
我无法确定这里出了什么问题?有人能帮忙吗。如果需要更多的细节或者有什么不清楚的地方,请告诉我。提前谢谢。
编辑不确定它是否重要,但本地库是用C编写的。所以我们在这里处理C结构。

ttygqcqt

ttygqcqt1#

根据这里的另一个答案,我可以通过传递来解决这个问题 GuidType.ByValuegetDataObjectType2ForGuid . 守则的修订如下:

class GuidType extends Structure {
   public static class ByValue extends GuidType implements Structure.ByValue {
        public ByValue() { }

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

   // ... other existing code
}
//  Change in signature of getDataObjectType2ForGuid to use GuideType.ByValue
int getDataObjectType2ForGuid(GuidType.ByValue guid, DataObjectType2**dataObjectType2);

// Code to invoke getDataObjectType2ForGuid()
result = libraryInstance.getDataObjectType2ForGuid(
             new GuidType.ByValue(dataObjectType1.guid.getPointer()), 
             dataObjectType2PointerByReference);

感谢@daniel widdis对我们的支持。

7qhs6swi

7qhs6swi2#

我最初的回答是,您需要为 PointerByReference . 这是一个错误的建议,因为正如您后来指出的,api告诉您它将分配自己的内存并返回指向它的指针。
正如您所观察到的,您遇到了一种不寻常的情况,c库希望通过值传递结构。关键线索在于此方法签名:

int getDataObjectType2ForGuid(GuidType guid, PointerByReference dataObjectType2Instance);

通常,当您在本机方法调用中看到一个结构时,它是一个指向数据的指针,因此您会看到 GuidType *guid .
默认情况下,jna处理结构 ByValue 在其他结构中声明时(例如在 DataObjectType2 )及 ByReference 传入方法参数时(通常具有 * 指示一个指针——你会看到的 GuidType *guid ).
如果本机api不同于此默认约定(结构中基于指针的变量或方法调用中的完整结构),则需要显式标记结构 ByValueByReference .
这在jna概述的“结构”标题下进行了讨论。

相关问题