Передача класса Java в параметр void* с помощью JNA

у меня есть функция в C, которую я пытаюсь вызвать из Java с JNA:

int myCfunc(void *s, int *ls);

по словам документация JNA пустота* требуется com.sun.jna.Pointer быть передано функции. В java с JNA я считаю, что вышеуказанная функция будет обернута следующим образом:

public interface myWrapper extends Library{ 
    public int myCfunc(Pointer s, IntByReference ls);
}

объекты, которые нужно связать с указателем и передать в параметре s будут классы, которые реализуют структуру JNA, такую as:

public class myClass extends Structure{
    public int x;
    public int y;
    public int z;
}

к сожалению, параметр ls - это целое число, представляющее длину класса в байтах. Java не было!--7--> функция, так что это добавляет немного сложности. Другая серьезная проблема, с которой я сталкиваюсь, - это правильная передача содержимого объекта в собственную память и обратно.

мой код похож на следующий:

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.ptr.IntByReference;

public void foo(){
    myWrapper wrapper = (myWrapper) Native.loadLibrary("SomeDllWithLegacyCode", myWrapper.class);

    myClass myObj = new myClass();
    myObj.x = 1;
    myObj.y = 2;
    Pointer myPointer = myObj.getPointer();

    int size = Native.getNativeSize(myClass.class);
    IntByReference len = new IntByReference(size);

    myObj.write(); //is this required to write the values in myObj into the native memory??
    wrapper.myCfunc(myPointer, len);
    myObj.read();  //does this read in the native memory and push the values into myObj??

    myPointer.clear(size); //is this required to clear the memory and release the Pointer to the GC??
}

Я это переданные данные больший размер, чем ожидается С помощью функции C.

приведенный выше код примерно следует тем же шагам как указано в ответе к вопросу, касающемуся аналогичной проблемы, но в C#. Я пробовал и проверял, что он работает на C#.

мой вопрос похож на еще один на Stackoverflow, но это касается указателя на IntByReference, а не указателя на класс.

1 ответов


прежде всего, JNA автоматически обрабатывает собственные выделения памяти, что означает, что следующая строка бесполезна (и может повредить стек памяти):

myPointer.clear(size); //is this required to clear the memory and release the Pointer to the GC??

далее он также автоматически имеет дело с собственными типами указателей, что означает, что обе строки ниже эквивалентны в вашем случае:

public int myCfunc(Pointer s, IntByReference ls);
public int myCfunc(myClass s, IntByReference ls);

и таким образом JNA будет делать myObj.write(); и read для вас.

ниже 100% правильно, но я предлагаю вам войти len.getValue() до и после вызов myCfunc (который должен дать 3 * 4=12; 3 int 4 байта):

int size = Native.getNativeSize(myClass.class);
IntByReference len = new IntByReference(size);

если все это правильно, то у вас, вероятно, есть ошибка в прототипе вашей структуры.

по моему опыту, это в основном связано с устаревшим заголовочным файлом C или устаревшей библиотекой:

  • вы используете заголовок версии 2.0, в котором говорится, что значения структуры int, но ваша связь с библиотекой v1.0, который принимает структуру байтов
  • вы используете заголовок версии 2.0, в котором говорится, что значения структуры int, но ваша связь с библиотекой v1.9 который принимает два Инта и Байт

в итоге код должен выглядеть так :

public void foo(){
    myWrapper wrapper = (myWrapper) Native.loadLibrary("SomeDllWithLegacyCode", myWrapper.class);

    myClass myObj = new myClass();
    myObj.x = 1;
    myObj.y = 2;

    int size = Native.getNativeSize(myClass.class);
    IntByReference len = new IntByReference(size);

    //log len.getValue
    wrapper.myCfunc(myObj, len);
    //log len.getValue
}

вы также можете попробовать добровольно уменьшить значение len для целей отладки например :

IntByReference len = new IntByReference(size-1);
IntByReference len = new IntByReference(size-2);
IntByReference len = new IntByReference(size-3);
//...
IntByReference len = new IntByReference(size-11);

это не будет делать то, что вы не хотите, но, по крайней мере, это должно дать вам правильный "Макс лен"