我在 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
引用您的评论:
从 API 来看,JNACreator 给出了正确的结果。
、SqImageInfo**
和SqPicInfo**
都是SqSliceInfo**
中的结构体指针数组。长度取决于SqWholeImageInfo
,所以我需要先获取SqPicHead *phead中的信息,然后才能获取SqPicInfo **sliceLayerInfo的长度。int SqPicHead.hierarchy
这是用于映射的关键信息:
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
版本,因为你不需要它