В настоящее время я работаю над проектом, который требует разработки нативной DLL (в C ++) для доступа к приложению Java. Я выбрал JNA для работы моста, и у меня возникают проблемы с передачей правильных значений int из Java в функции C ++.
Проще говоря, у меня есть функция, которая принимает значение int в качестве параметра в C ++:
(код удаляется, а методы переименовываются для сохранения конфиденциальности)
JAVALINK_EXPORT SomeStructure WINAPI GetSomeStructureFromIndex(int index) {
std::string debugMsg("Received index of ");
debugMsg.append(toString(index));
OutputDebugString(debugMsg.c_str());
SomeStructure result = defaultStructure;
if (index >= 0 && index < structListSize)
result = structList[index];
return result;
}
toString
это простой метод, который преобразует значение любого типа данных в std::string
используя std::stringstream
, Реализация заключается в следующем:
template <class T>
inline std::string toString (const T& t) {
std::stringstream ss;
ss << t;
return ss.str();
}
SomeStructure
переименован из фактической структуры, которую я использую в коде. structList
это массив SomeStructure
, structListSize
а также structList
все глобальные переменные в разделяемой памяти.
Это сигнатура метода в интерфейсе Java для DLL:
SomeStructure.ByValue GetSomeStructureFromIndex(int index);
Вот как я использую метод в Java для теста:
SomeStructure.ByValue received = library.GetSomeStructureFromIndex(1);
library
это экземпляр интерфейса для файла DLL (подкласс StdCallLibrary
) создан с использованием Native.loadLibrary
, Когда приведенный выше код выполняется в Java, я получаю что-то вроде следующего вывода в моем выводе отладки Windows:
Received index of 86701080
(Затем программа перейдет к ошибке нарушения прав доступа, если я пропустил проверку index
с линией if (index >= 0 && index < structListSize)
до получения структуры из массива)
86701080
может быть любым произвольным значением. Я понял, что это меняется в зависимости от сигнатуры экспортируемой функции. Я что-то здесь упускаю? Функция правильно получает ожидаемое значение 1
была подпись функции void PrintIndex(int index)
РЕДАКТИРОВАТЬ (0): я изменил пример кода для более точного соответствия с реальным кодом.
РЕДАКТИРОВАТЬ (1): В соответствии с указателями @ technomage я начал использовать ByValue
для всех сигнатур методов и переменных, собирающих возвращаемые структуры.
РЕДАКТИРОВАТЬ (2): класс Java SomeStructure
имеет дополнительную переменную и метод Java по сравнению с SomeStructure
структура в C ++. В настоящее время я проверяю, является ли это причиной расхождения.
ЗАДАЧА РЕШЕНА
@technomage пояснил, что для того, чтобы функция C ++ могла интерпретировать свои аргументы и возвращаемые значения так, как ожидается, размер структуры, используемой в качестве возвращаемого типа (а также в качестве параметров функции), не должен отличаться от ее Java-аналога. Это может быть проверено, в случае SomeStructure
в C ++ с sizeof(SomeStructure)
и в Java с SomeStructure.size()
,
В основном, случилось то, что SomeStructure
Структура имеет другой размер, чем ее представление Java. SomeStructure
содержит массив фиксированной длины, как показано в коде ниже:
#define MAX_LIST_SIZE 256
typedef struct {
int list[MAX_LIST_SIZE];
int length;
} SomeStructure;
Однако в представлении Java не указан размер массива фиксированной длины. list
был инициализирован, чтобы содержать одно значение 0
,
package model;
import com.sun.jna.Structure;
public class SomeStructure extends Structure {
public static class ByValue extends SomeStructure implements Structure.ByValue { }
public int[] list = {0};
public int length = 0;
}
Я решил проблему, заменив неверный оператор инициализации следующим:
private static final int MAX_LIST_SIZE = 256;
public int[] list = new int[MAX_LIST_SIZE];
Заметка: Целочисленная константа MAX_LIST_SIZE
объявлен private
сохранить его только для Java.
После внесения всех этих изменений мой код работает нормально и больше не сталкивается с нарушениями доступа.
«WINAPI» подразумевает соглашение о вызовах stdcall, но вам нужно убедиться в локальном определении макроса. Если это так, вам нужно StdCallLibrary
вместо Library
, Это может повлиять на ваш входящий index
аргумент.
Вы также копируете свою структуру (по семантике значения), а не указатель на нее. Когда вы передаете структуру по значению или возвращаете структуру по значению, вам необходимо скажи JNA, что ты так делаешь.
РЕДАКТИРОВАТЬ
Удостовериться SomeStructure.size()
соответствует родной sizeof(SomeStructure)
, Обычно структуры, возвращаемые по значению, реализуются таким образом, что вызывающая сторона выделяет память в стеке и передает неявный указатель вызываемому объекту, который затем записывает в эту память. Если вызывающая сторона и вызываемая сторона не согласны с размером этой памяти, это может повлиять на другие вещи в стеке (такие как аргументы и возвращаемые значения). Вы также можете добавить еще несколько аргументов в функцию и распечатать их значения (в шестнадцатеричном виде), чтобы получить приблизительное значение того, что находится в стеке. Если вы передаете узнаваемые аргументы (например, 0x12345678), часто становится ясно, что толкает или тянет стек в неправильном направлении.
Других решений пока нет …