Передача Java-класса в параметр void * с помощью JNA - PullRequest
10 голосов
/ 04 ноября 2010

У меня есть функция в 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, такими как:

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

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

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

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 Ответ

6 голосов
/ 24 декабря 2010

Прежде всего, 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, но ваш linking против библиотеки 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);

Это не будет делать то, что вы не хотите, но, по крайней мере, оно должно дать вам правильный "max len"

...