Я пытаюсь отобразить структуру, содержащую массив структур в JNA. Размер встроенной структуры определяется не в расширенном объявлении структуры C++, а в коде Java. Моя проблема в том, что я получаю исключение в потоке main java.lang.Error: Invalid memory access
Заголовочный файл C++ выглядит следующим образом:
typedef struct s_Param
{
char* key;
uint32_t key_value;
} Param;
typedef struct s_ParamList {
Param* init_param;
int param_list_size; // number of param items in the param List
} ParamList;
Код С++ выглядит следующим образом:
int Init(
ParamList* i_initParamList,
logfunction i__callback,
const allocator* i__allocator,
void** o__content) {
...
if (i_initParamList != NULL){
printf("DLL PRINT init ---- i_initParamList = %p\n", i_initParamList);
printf("DLL PRINT init ---- i_initParamList->param_list_size = %i\n", i_initParamList->param_list_size);
if (i_initParamList->init_param == NULL){
printf("DLL PRINT init ---- i_initParamList->init_param must not be NULL\n");
returnedCode = 1;
}else{
for (int i = 0; i<i_initParamList->param_list_size;i++){
printf("DLL PRINT init ---- i_initParamList->init_param[i]->key = %s\n",i_initParamList->init_param[i].key);
printf("DLL PRINT init ---- i_initParamList->init_param[i]->key_value = %i\n",i_initParamList->init_param[i].key_value);
}
...
}
Код java JNA выглядит следующим образом:
public interface MyLibrary extends Library {
@FieldOrder({ "key", "key_value" })
public static class Param extends Structure {
public static class ByReference extends Param implements Structure.ByReference {
}
public String key;
public int key_value;
public Param(){ // NOT sure that this adding constructor makes sense
super();
setAlignType(ALIGN_NONE);
}
}
@FieldOrder({ "init_param", "param_list_size" })
public static class ParamList extends Structure {
public static class ByReference extends ParamList implements Structure.ByReference {
}
public Param[] init_param;
public int param_list_size;
ParamList(){ // NOT sure that this adding constructor makes sense
super();
setAlignType(ALIGN_NONE);
}
}
public int Init(ParamList i_initParamList, logfunction i__callback, allocator i__allocator,
PointerByReference o__content);
...
}
Код Sample.java, который вызывает библиотеку JNA, выглядит следующим образом: Обратите внимание, что я не добавлял способ управления другими параметрами функции Init, поскольку у меня нет с ними проблем.
int paramListSize = 4;
MyLibrary.Param[] params = new MyLibrary.Param[paramListSize];
for (int i = 0; i < paramListSize; i++) {
params[i] = new MyLibrary.Param();
}
params[0].key = "first";
params[0].key_value = 1;
params[1].key = "second";
params[1].key_value = 5;
params[2].key = "third";
params[2].key_value = 7;
params[3].key = "forth";
params[3].key_value = 9;
MyLibrary.ParamList paramList = new MyLibrary.ParamList.ByReference();
paramList.init_param = params;
paramList.param_list_size = paramListSize;
logger.debug("params = "+ params);
logger.debug("paramList = "+ paramList);
logger.debug("paramList.param_list_size = "+paramList.param_list_size);
int errInit = IFunctions.Init(paramList, logCallback, i__allocator, o__content);
Результаты трассировки, поступающие из кода C++ и Java, следующие:
10:41:28,303 DEBUG Sample:193 - params = [MyLibrary$Param;@1e67a849
10:41:28,312 DEBUG Sample:194 - paramList = MyLibrary$ParamList$ByReference(auto-allocated@0x1f3d49fe5b0 (52 bytes)) {
MyLibrary$Param init_param[4]@0x0=[MyLibrary$Param;@1e67a849
int param_list_size@0x30=0x0004
}
10:41:28,316 DEBUG Sample:195 - paramList.param_list_size = 4
Exception in thread "main" java.lang.Error: Invalid memory access
at com.sun.jna.Native.invokeInt(Native Method)
at com.sun.jna.Function.invoke(Function.java:426)
at com.sun.jna.Function.invoke(Function.java:361)
at com.sun.jna.MyLibrary$Handler.invoke(MyLibrary.java:265)
at com.sun.proxy.$Proxy3.init(Unknown Source)
at Sample.main(Sample.java:216)
DLL PRINT init ---- i_initParamList = 000001F3D49FE5B0
DLL PRINT _init ---- init_param address = 0000021B4ADAF3D0
DLL PRINT init ---- i_initParamList->param_list_size = 1
DLL PRINT init ---- i_initParamList->init_param[i]->key =
Это работает, если я объявлю свою структуру в заголовке C++ следующим образом:
typedef struct s_ParamList {
Param init_param[4];
int param_list_size; // number of param items in the param List
} ParamList;
Но я не хочу определять размер массива init_param в коде C++, поскольку он должен быть определен на стороне кода Java.
Что касается добавленного следующего кода в javacode:
ParamList(){ // NOT sure that this adding constructor makes sense
super();
setAlignType(ALIGN_NONE);
}
Я не уверен, что мне нужно добавить это в обе структуры, то есть в ParamList и в структуру Param. Но в любом случае, если я удалю оба этих конструктора, у меня будет точно такая же проблема.
Я увидел другой способ управления моим требованием (посмотрев на метод toArray(size) из главы Структура https://javadoc.io/doc/net.java.dev.jna/jna/latest/index.html для версии 5.8.0 JNA. Действительно, я также перешел по ссылке Как заполнить массив структур в JNA? но я получаю недействительный доступ к памяти.Я создам отдельный вопрос относительно этого второго вида реализации, если я не могу иметь массив моей структуры в структуре.И мне пришлось разделить массив Param и размер массива на 2 отдельных параметра Функция инициализации вместо того, чтобы устанавливать их в уникальную структуру.И поскольку у меня уже есть много параметров в моей функции инициализации, я бы предпочел иметь только один параметр структуры.И я думаю, что это должно быть возможно в JNA.
У кого-нибудь есть ключ?
Спасибо за вашу помощь.
init_param
является указателем. Это особый случай, когда вам нужно использовать версию структуры по ссылке,Param.ByReference
. Настройка типа выравнивания необязательна. В вашем вопросе отсутствует информация о том, где выделена память для массива параметров. Вы не показываете это в своем коде C++. Обычно API сообщает вам, когда выделяется память и как ее освободить. Но ваша структура просто имеет указатель на память в другом месте и целое число, говорящее, сколько - person Daniel Widdis   schedule 31.03.2021params[i] = new MyLibrary.Param();
- person cknelle   schedule 31.03.2021set_param_size(int param_size)
. Выполните malloc для размера C++. ШАГ 2 => замените первый параметр Init следующим образомint Init(Param* inOut_initParam,...)
и заполните содержимое первого параметра на стороне java, используя адрес, указанный на стороне C++. ШАГ 3 => Выполните бесплатное выделение таблицы структуры inOut_initParam после использования на стороне C++. - person cknelle   schedule 31.03.2021ParamList
должно быть либоParam.ByReference
(указывающим на первый элемент массива, который вы выделяете), либо вы можете использовать тамPointer
. Вам придется вручную выполнитьread()
, так что это то, что вы поместите в свой конструктор... прочитайте размер, а затем прочитайте количество байтов. Я напишу это в полном ответе позже. - person Daniel Widdis   schedule 31.03.2021