Использование JNI

Взаимодействие кодов JAVA и С/С++ может осуществляться двумя способами: С/С++-код получает управление непосредственно из JAVA-программы путем вызова собственного (NATIVE) метода; С/С++-код динамически загружает JVM с помощью INVOCATION API.

Сентябрь 21, 2008 — Filed under: C++
Метки: , ,

Использование INVOCATION API

Использование INVOCATION API позволяет встраивать JVM в приложения без необходимости их статического связывания с кодом самой JVM. Напомним, что в этом случае управление изначально находится в С/С++-программе. INVOCATION API состоит из небольшого набора функций, позволяющих создавать и уничтожать JVM в текущем процессе, присоединять и отсоединять текущий поток от JVM (интерфейсный указатель существует только в рамках данного потока).

Сентябрь 10, 2008 — Filed under: C++
Метки: , ,

JNI функции

JNI определяет 210 прикладных функций. Доступ к ним из С/С++-функции можно получить через интерфейсный указатель JNIENV*, который передается каждой С/С++-функции, представлющей реализацию собственного метода. Все функции разделены на 14 групп:
информация о версии JNI;
операции с классами;
исключения (EXCEPTIONS);
обработка глобальных и локальных ссылок;
операции с объектами;
доступ к данным объекта;
вызов методов объекта (INSTANCE METHOD);
доступ к статическим данным объекта;
вызов методов класса (CLASS METHOD);
операции со строковыми объектами;
операции с массивами;
регистрация собственных методов;
операции с мониторами (MONITOR OPERATIONS);
интерфейс с JVM.

Использование JNI функций необходимо только в том случае, если С/С++-функция осуществляет какое-либо взаимодействие с JVM: вызов JAVA-методов, доступ к данным, создание JAVA-объектов и т.д.

Ниже приведен пример JAVA-программы, которая выводит на печать количество свободной памяти на диске С для платформы WIN32. Для этого используется собственный метод и соответствующая реализационная С/С++-функция, вызывающая при своей работе функцию WIN32 API.
// Файл APP.JAVA
PUBLIC CLASS APP {
PUBLIC STATIC VOID MAIN(STRING ARGS[]) {
SYSTEMSPECIFIC SS = NEW SYSTEMSPECIFIC();
TRY {
LONG BYTES = SS.GETCDRIVEFREESPACE();
IF (BYTES != -1) {
LONG KB = BYTES / 1024;
SYSTEM.OUT.PRINTLN(«на диске C:\\ свободно » + KB + » KB»);
}
ELSE {
SYSTEM.OUT.PRINTLN(«произошла ошибка в С/С++-функции»);
}
}
CATCH (UNSATISFIEDLINKERROR E) {
SYSTEM.OUT.PRINTLN(«метод не найден (» + E + «)»);
}
}
}
CLASS SYSTEMSPECIFIC {
STATIC {
TRY {
SYSTEM.LOADLIBRARY(«SYSSPEC»);
}
CATCH (UNSATISFIEDLINKERROR E) {
SYSTEM.OUT.PRINTLN(«библиотека не найдена (» + E + «)»);
}
}
NATIVE LONG GETCDRIVEFREESPACE();
}
// Файл SYSTEMSPECIFIC.CPP
#INCLUDE «SYSTEMSPECIFIC.H»
#INCLUDE
JNIEXPORT JLONG JNICALL JAVA_SYSTEMSPECIFIC_GETCDRIVEFREESPACE (JNIENV *, JOBJECT) {
DWORD SCTRPERCLSTR, BYTESPERSCTR, FREECLSTR, CLSTR;
BOOL RES = GETDISKFREESPACE(«C:\\», &SCTRPERCLSTR, &BYTESPERSCTR, &FREECLSTR, &CLSTR);
RETURN RES == TRUE ? SCTRPERCLSTR * BYTESPERSCTR * FREECLSTR : -1;
}

Для успешной компиляции кода JAVA и С/С++ на платформе WIN32 необходимо правильно установить переменные окружения PATH, LIB, INCLUDE и CLASSPATH (многие из этих значений можно задать как параметры соответствующих компиляторов).

Если записать исходные тексты предыдущего примера в файлы APP.JAVA и SYSTEMSPECIFIC.CPP соответственно, то для их компиляции необходимо выполнить следующие команды (предполагается, что исходные файлы находятся в каталоге C:\TEST\NATIVE):
C:\TEST\NATIVE> JAVAC APP.JAVA
C:\TEST\NATIVE> JAVAH -JNI SYSTEMSPECIFIC
C:\TEST\NATIVE> CL -W3 SYSTEMSPECIFIC.CPP -FESYSSPEC.DLL -TP -LD -MD -LINK JAVAI.LIB

Для запуска программы необходимо выполнить: C:\TEST\NATIVE> JAVA APP
на диске С:\ свободно 324567 KB
C:\TEST\NATIVE>

Для трансляции С/С++-файлов можно использовать любой компилятор, допускающий создание 32-битных DLL.

Май 7, 2008 — Filed under: C++
Метки: ,

Типы и структуры данных JNI

JNI использует целый набор типов для своих функций и для формальных параметров С/С++-функций, представляющих реализацию собственных методов. Все эти типы описаны в файле JNI.H, который включается в любой заголовочный файл для JNI. Файл JNI.H использует стандартную технику препроцессирования с макросом _CPLUSPLUS. Тем самым, в зависимости от того, какой (С++ или С) код компилируется, будут создаваться две немного отличающиеся версии описания типов. Каждая из них требует определенного синтаксиса доступа.

Файл JNI_MD.H содержит системно-зависимые описания JINT, JLONG и JBYTE. В этом же файле определены макросы JNIEXPORT и JNICALL. Тип VOID используется без переопределения.

Следует отметить, что для представления строковых объектов JNI использует сокращенный вариант формата UTF-8.

Первым аргументом С/С++-функции, представляющей реализацию собственного метода, является указатель на структуру JNIENV. Смысл этого указателя определяет важную идею, лежащую в основе реализации JNI. Если отвлечься от конкретного языка, то указатель на JNIENV, или интерфейсный указатель (JNI INTERFACE POINTER), является указателем на массив указателей, каждый из которых указывает на прикладную функцию JNI. Только через этот указатель С/С++-функция может получить доступ к функциям и ресурсам JNI. В случае С++ (макрос _CPLUSPLUS определен) тип JNIENV является структурой, а в случае С — указателем на структуру. В силу этого, для доступа к функциям JNI в С и С++ применяется различный синтаксис:
// C
JNIEXPORT VOID JNICALL JAVA_SYSTEMSPECIFIC_DOSPECIFIC(JNIENV* ENV, JOBJECT THIS) {
JINT VERSION = (*ENV)->GETVERSION(ENV);
Е
}
// C++
JNIEXPORT VOID JNICALL JAVA_SYSTEMSPECIFIC_DOSPECIFIC(JNIENV* ENV, JOBJECT THIS) {
JINT VERSION = ENV->GETVERSION();
Е
}

Главным преимуществом такой организации функций JNI является легкость модификации и дальнейшего расширения интерфейса.

Указатель на JNIENV действителен только в текущем потоке (THREAD). JVM гарантирует передачу одного и того же интерфейсного указателя всем методам, вызываемым из данного потока. Тем самым запрещено передавать интерфейсный указатель другому потоку. Если методы вызываются из разных потоков, то в этом случае каждый метод получает различные интерфейсные указатели.

Если С/С++-функция представляет реализацию нестатического собственного метода, то вторым параметром функции является объект типа JOBJECT. Данный параметр является ссылкой на JAVA-объект, для которого был вызван соответствующий собственный метод. Если функция представляет статический собственный метод, то вторым параметром является объект типа JCLASS, определяющий JAVA-класс, для которого вызван собственный метод класса (CLASS METHOD).

Последующие параметры С/С++-функции соответствуют параметрам собственного метода (если собственный метод их не содержит, то реализующая его С/С++-функция имеет только два описанных выше параметра).

Май 3, 2008 — Filed under: C++
Метки: ,

Сигнатура метода

Сигнатура метода — это сокращенная форма записи параметров метода и типов возвращаемого значения. Следует подчеркнуть, что в сигнатуру не входят ни имя метода, ни имена параметров. JNI формирует сигнатуры в соответствии с правилами, представленными в табл. 1.

Таблица 1Знак сигнатуры JAVA-тип
Z BOOLEAN
B BYTE
C CHAR
S SHORT
Internet INT
J LONG
F FLOAT
V VOID
D DOUBLE
L полное квалифицированное имя класса полное квалифицированное имя класса
[ тип тип[]
(типы аргументов) возвращаемый тип полная сигнатура метода

Проиллюстрируем эти правила на примерах:
метод LONG M1(INT N, STRING S, INT[] ARR);
сигнатура (ILJAVA/LANG/STRING;[I)J;
метод VOID M2(FLOAT N, BYTE[][] ARR, RUNTIME R);
сигнатура (F[[BLJAVA/LANG/RUNTIME;)V.

Полная информация о правилах образования сигнатуры метода представлена в файле SIGNATURE.H.

Апрель 30, 2008 — Filed under: C++
Метки: ,