C++ и Java: совместное использование

ОГЛАВЛЕНИЕ

Язык JAVA во многом произошел от С/С++, у которых были позаимствованы синтаксис и базовая семантика. Однако связь между ними не ограничивается только этим. Используя JNI (JAVA NATIVE INTERFACE), можно вызывать С/С++-функции из JAVA-программы и, наоборот, из программы, написанной на С/С++, можно создавать JAVA-объекты и вызывать JAVA-методы. Несмотря на то, что использование JNI в большинстве случаев ведет к потере многоплатформенности JAVA-кода, данная возможность расширяет сферу применения самого языка JAVA на приложения, для которых это условие не является необходимым. В таких системах использование JNI позволяет сочетать современный объектно-ориентированный подход JAVA - главное преимущество этой технологии, с существующим (LEGACY) системно-зависимым (PLATFORM SPECIFIC) кодом на С/С++. Это является важным и необходимым условием перехода к использованию JAVA-технологии при разработке компонентов сервера. Существует несколько причин совместного использования С/С++ и JAVA: стандартные библиотеки JAVA-классов не всегда поддерживают некоторые системно-зависимые возможности; необходимость использования наработанного и отлаженного кода на других языках или желание максимально эффективно реализовать участок кода, критичного с точки зрения времени исполнения. Эти причины не существенны при разработке клиентских приложений, однако в случае серверных - они становятся доминирующими.

Для обеспечения интероперабельности программного кода в рамках С/С++ и JAVA JDK1.1 (JAVA DEVELOPERS KIT) предоставляет набор интерфейсов, объединенных в JNI (JAVA NATIVE INTERFACE). JNI позволяет JAVA-коду, исполняемому виртуальной JAVA-машиной (JVM - JAVA VIRTUAL MACHINE), взаимодействовать с приложениями и библиотеками, написанными на языках С/С++ или Ассемблера.

Основным преимуществом JNI перед предыдущей версией (JDK 1.0 NI - NATIVE INTERFACE) и другими сходными интерфейсами (NETSCAPE JAVA RINTIME INTERFACE или MICROSOFT'S RAW NATIVE INTERFACE AND COM/JAVA INTERFACE) является то, что JNI изначально разрабатывался для обеспечения двоичной совместимости (BINARY COMPATIBILITY), подразумевающей совместимость приложений, написанных с использованием JNI, для любых JVM на конкретной платформе. Другими словами, один и тот же скомпилированный С/С++-код должен одинаково корректно исполняться JVM NETSCAPE NAVIGATOR и MICROSOFT EXPLORER, SYMANTEC VISUAL CAFО и SUN JAVA WORKSHOP и т.д. для данной платформы (WIN32). Следует отметить, что ранние интерфейсы не удовлетворяли этому условию. Например, JDK 1.0 NI, входящий в JDK 1.0.2 и поддерживаемый в JDK 1.1 для обратной совместимости, использует С-структуры для доступа к членам JAVA-объекта, что определяет зависимость С/С++-кода от того, как JVM располагает объекты в памяти. В общем случае, при использовании JDK 1.0 NI требуется перекомпиляция соответствующего С/С++-кода для каждой JVM на данной платформе.

Несмотря на определенную универсальность интерфейса, обусловленную его двоичной совместимостью, JNI обладает широкой функциональностью, предоставляя разработчику все низкоуровневые механизмы JVM: создание JAVA-объектов, включая создание массивов и объектов типа STRING; вызов JAVA-методов; возбуждение и перехват исключительных ситуаций (EXCEPTION); загрузка JAVA-классов и динамический анализ типа (RUNTIME TYPE CHECKING). Отдельно в JNI входит INVOCATION API, позволяющий приложениям динамически загружать JVM. Динамическая загрузка JVM из С/С++-кода позволяет легко встраивать возможности JAVA в существующие системы без необходимости их статического связывания (LINKAGE) с кодом JVM.

Ниже будет рассмотрено, как создавать коды на С/С++ и JAVA для их совместного использования в рамках JNI и INVOCATION API. Все примеры разработаны и протестированы на платформе WINDOWS 95. Во всех случаях, когда это необходимо, даются пояснения для платформы UNIX.

JNI определяется библиотечными и заголовочными (HEADER) файлами для С/С++. Библиотечные файлы хранятся в подкаталоге LIB (DLL - DYNAMIC-LINK LIBRARY, для WIN32 - в подкаталоге BIN), а заголовочные файлы - в подкаталоге INCLUDE основного каталога JAVA.


 

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

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

Рассмотрим первую из указанных возможностей.

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


 

Создание собственного JAVA-метода

Собственный метод создается путем добавления к его описанию спецификатора NATIVE, при этом он не должен иметь реализации (так же как и методы в описании интерфейса). Спецификатор NATIVE сообщает компилятору, что реализация данного метода будет представлена в виде откомпилированного С/С++-кода, помещенного в библиотечный файл. Когда JVM встречает обращение к собственному методу, происходит вызов соответствующей С/С++-функции. Помимо описания собственнного метода, JAVA-код должен динамически загрузить библиотеку, содержащую С/С++-функцию с реализацией данного метода. Для этого в классе JAVA.LANG.SYSTEM существует метод PUBLIC STATIC VOID LOADLIBRARY (STRING LIBNAME), загружающий указанную библиотеку. Следующий пример демонстрирует описание собственного метода.

CLASS SYSTEMSPECIFIC {
STATIC {
SYSTEM.LOADLIBRARY("SYSSPEC");
}
NATIVE VOID DOSPECIFIC();
}

В приведенном примере метод DOSPECIFIC() является собственным, и его С/С++-реализация находится в библиотеке SYSSPEC. Метод LOADLIBRARY() вызывается в статическом инициализаторе, что обеспечивает единственный вызов этого метода после загрузки класса SYSTEMSPECIFIC загрузчиком классов (CLASS LOADER). В принципе, LOADLIBRARY() можно вызывать более одного раза (например, в конструкторе), однако загрузка библиотеки будет происходить только при первом обращении к LOADLIBRARY(), поскольку при последующих вызовах этого метода определяется, что библиотека уже загружена и будет просто возвращаться управление.

Метод LOADLIBRARY() преобразует свой параметр в соответствии с тем, как именуются библиотечные файлы на конкретной платформе. В данном примере SYSSPEC преобразуется в SYSSPEC.DLL и LIBSYSSPEC.SO для WIN32 и UNIX соответственно. Метод LOADLIBRARY() использует стандартный алгоритм поиска библиотеки для данной платформы. Для WIN32 DLL должна находиться либо в текущем каталоге процесса, либо в каталоге, содержащем EXE-файл, то есть исполняемый модуль JVM, находящийся в подкаталоге BIN основного каталога JAVA, либо в системном каталоге WIN32, либо каталоге WINDOWS или в каталогах, указанных в переменной окружения PATH. Для UNIX библиотечный файл должен находиться либо в текущем каталоге процесса, либо в подкаталоге LIB основного каталога JAVA, либо в каталогах, перечисленных в переменной окружения LD_LIBRARY_PATH. Если указанную библиотеку найти не удается, метод LOADLIBRARY() генерирует исключительную ситуацию JAVA.LANG.UNSATISFIEDLINKERROR. Однако данная ситуация возникает не только в этом случае. Когда интерпретатор встречает вызов собственного метода, он ищет его (точнее его полную сигнатуру) в списке методов загруженных библиотек. Если метод не найден, то генерируется указанная исключительная ситуация.

Для более надежной работы с собственными методами можно использовать, к примеру, следующий код:

PUBLIC CLASS APP  {
PUBLIC STATIC VOID MAIN(STRING ARGS) {
SYSTEMSPECIFIC SS = NEW SYSTEMSPECIFIC();
TRY {
SS.DOSPECIFIC();
}
CATCH (UNSATISFIEDLINKERROR E) {
SYSTEM.OUT.PRINTLN("метод не найден (" + E + ")");
}
}
}
CLASS SYSTEMSPECIFIC {
STATIC {
TRY {
SYSTEM.LOADLIBRARY("SYSSPEC");
}
CATCH (UNSATISFIEDLINKERROR E) {
SYSTEM.OUT.PRINTLN("библиотека не найдена (" + E + ")");
}
}
NATIVE VOID DOSPECIFIC();
}

Компиляция программ, содержащих собственные методы, ничем не отличается от компиляции обычных программ. Например, если записать предыдущий пример в файл с именем APP.JAVA, то для его компиляции необходимо выполнить следующую команду:

C:\ JAVAC  APP.JAVA


 

Создание заголовочного файла

Создание С/С++-кода необходимо начинать с создания заголовочного файла. Его можно написать вручную или воспользоваться утилитой JAVAH. Второй путь предпочтительней, так как допускает меньшее количество ошибок. При обращении к утилите JAVAH указывается имя класса и параметр -JNI. Без него JAVAH будет генерировать файл в формате JDK 1.0 NI. Имя класса представляет собой полное квалифицированное имя класса. Например:

JAVAH -JNI JAVA.LANG.RUNTIME

Перед использованием утилиты JAVAH соответствующий JAVA-класс должен быть скомпилирован в CLASS-файл. Утилита JAVAH анализирует CLASS-файл и строит заголовочный файл, в котором перечислены объявления С/С++-функций, представляющих реализации соответствующих собственных методов. В качестве имен создаваемых заголовочных файлов используются полные квалифицированные имена классов, которые описаны в указанном файле и содержат собственные методы. Например, если выполнить следующие команды:

JAVAC APP.JAVA
JAVAH -JNI SYSTEMSPECIFIC

то JAVAH сгенерирует следующий файл SYSTEMSPECIFIC.H:

/* DO NOT EDIT THIS FILE - IT IS MACHINE GENERATED */
#INCLUDE <JNI.H>
/* HEADER FOR CLASS SYSTEMSPECIFIC */
#IFNDEF _INCLUDED_SYSTEMSPECIFIC
#DEFINE _INCLUDED_SYSTEMSPECIFIC
#IFDEF _ _CPLUSPLUS
EXTERN "C" {
#ENDIF
/*
* CLASS: SYSTEMSPECIFIC
* METHOD: DOSPECIFIC
* SIGNATURE: ()V
*/
JNIEXPORT VOID JNICALL JAVA_SYSTEMSPECIFIC_DOSPECIFIC(JNIENV *, JOBJECT);
#IFDEF _ _CPLUSPLUS
}
#ENDIF
#ENDIF

Как указывалось выше, данный файл можно создать вручную или с помощью утилиты JAVAH. В последнем случае не рекомендуется вносить в него какие-либо изменения, так как при последующем применении JAVAH к данному классу все внесенные изменения будут потеряны.

Директива препроцессора #INCLUDE <JNI.H> включает файл JNI.H (из подкаталога INLCUDE основного каталога JAVA), в котором находятся все необходимые объявления типов и функций для реализации собственного метода.

Макросы JNIEXPORT и JNICALL необходимы только для платформы WIN32, где они раскрываются соответственно в __DECLSPEC(DLLEXPORT) и __STDCALL и позволяют более эффективно строить DLL. Платформа UNIX использует для этих целей обычные С-соглашения, поэтому указанные макросы раскрываются в пустые строки.

Как видно из примера, имя С/С++-функции значительно отличается от имени собственного JAVA-метода. Важным понятием при построении имени С/С++-функции и использовании JNI-функций является сигнатура метода (SIGNATURE или METHOD ARGUMENTS SIGNATURE).


 

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

Сигнатура метода - это сокращенная форма записи параметров метода и типов возвращаемого значения. Следует подчеркнуть, что в сигнатуру не входят ни имя метода, ни имена параметров. 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.


Правила формирования имени С/С++-функции

Имя С/С++-функции формируется путем последовательного соединения следующих компонентов:

  • префикс JAVA_;
  • полное квалифицированное имя класса;
  • символ подчеркивания ("_");
  • имя метода;
  • для перегружаемых (OVERLOADED) методов - два символа подчеркивания ("_ _") с последующей сигнатурой метода.

Использование имен с сигнатурой на конце необходимо только в случае перегрузки двух или более собственных методов (перегрузка с обычным методом не важна, так как обычные методы не будут находиться в создаваемой библиотеке, что, однако, не допускает наличия собственного и обычного метода с одинаковыми именами и сигнатурами).

Для соответствия лексиграфическим правилам С/С++ и использования UNICODE-кодировки, применяются дополнительные правила преобразования, представленные в табл. 2.

Таблица 2

Исходный символ Результирующая последовательность
"_" _1
";" _2
"[" _3
символ UNICODE с кодом ХХХХ _0ХХХХ

Ниже приведен пример JAVA-класса с собственными методами:

PACKAGE TESTPACKAGE;
ABSTRACT CLASS TEST {
PUBLIC NATIVE VOID M1(STRING[] SA, OBJECT O, INT[][] IA2);
PUBLIC NATIVE FLOAT[] M1(DOUBLE D, TEST T);
PUBLIC NATIVE TEST M3(INT I);
}

и соответствующие им имена С/С++-функций:

JNIEXPORT VOID JNICALL  JAVA_TESTPACKAGE_TEST_M1___3LJAVA_LANG_STRING_2LJAVA_LANG_OBJECT_2_3_3I
(JNIENV *, JOBJECT, JOBJECTARRAY, JOBJECT, JOBJECTARRAY);
JNIEXPORT JFLOATARRAY JNICALL JAVA_TESTPACKAGE_TEST_M1__LJAVA_LANG_DOUBLE_2LTESTPACKAGE_TEST_2
(JNIENV *, JOBJECT, JOBJECT, JOBJECT);
JNIEXPORT JOBJECT JNICALL JAVA_TESTPACKAGE_TEST_M3
(JNIENV *, JOBJECT, JINT);

Рассмотрим типы параметров, которые получает на входе С/С++-функция при ее вызове.


 

Типы и структуры данных 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).

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


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 <WINDOWS.H>
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.


 

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

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

В общем случае, для встраивания JVM в программу ее необходимо создать и проинициализировать, присоединить, если это необходимо, к какому-либо потоку, а по окончании работы с JVM удалить ее из памяти процесса. После того как JVM создана и получен интерфейсный указатель, можно использовать любые JNI-функции.

Рассмотрим пример С++-кода, который создает JVM в процессе своей работы и вызывает статический метод JAVA-класса. Ниже приведены исходные тексты JAVA-класса и C++-кода:

// Файл  INVOCATIONAP.JAVAI
PUBLIC CLASS INVOCATIONAPI {
STATIC VOID TEST() {
SYSTEM.OUT.PRINTLN("HELLO FROM JAVA CODE");
}
}
// Файл INVOCATIONAPI.CPP
#INCLUDE <JNI.H>
VOID MAIN() {
JAVAVM* JVM;
JNIENV* ENV;
// инициализация
JDK1_1INITARGS VMARGS;
JNI_GETDEFAULTJAVAVMINITARGS(&VMARGS);
VMARGS.CLASSPATH = "C:/JDK1.1/LIB/CLASSES.ZIP;C:/TEST/NATIVE";
// создание JVM
JNI_CREATEJAVAVM(&JVM, &ENV, &VMARGS);
// получение ссылки на класс INVOCATIONAPI
JCLASS CLS = ENV->FINDCLASS("INVOCATIONAPI");
// вызов статического метода TEST
JMETHODID MID = ENV->GETSTATICMETHODID(CLS, "TEST", "()V");
ENV->CALLSTATICVOIDMETHOD(CLS, MID);
// удаление JVM
JVM->DESTROYJAVAVM();

Для компиляции приведенной программы на платформе WIN32 необходимо выполнить следующие команды (предполагается, что переменные окружения PATH, LIB, INCLUDE и CLASSPATH установлены верно и исходные файлы находятся в каталоге C:\TEST\NATIVE):

C:\TEST\NATIVE\ JAVAC INVOCATIONAPI.JAVA
C:\TEST\NATIVE\ CL -W3 -NOLOGO INVOCATIONAPI.CPP -FEINVOCATIONAPI -TP -MD -LINK
-NOLOGO JAVAI.LIB

А для запуска программы:

C:\TEST\NATIVE\ INVOCATIONAPI
HELLO FROM JAVA CODE
C:\TEST\NATIVE\

На первый взгляд, наличие JNI и полная публикация его спецификаций может привлечь разработчиков к написанию непереносимого JAVA-кода, что, в свою очередь, может значительно снизить эффективность применения технологии JAVA. Однако на самом деле JNI способствует обратному процессу.

Практически для любого приложения можно совершенно точно определить необходимость в многоплатформенном исполнении. Для систем, которые не нуждаются в многоплатформенности, JNI предоставляет инфраструктуру, с помощью которой JAVA-приложение может взаимодействовать с операционной системой и аппаратурой, в среде которых оно исполняется. Таким образом, JNI является естественным дополнением JAVA-технологии. Он позволяет использовать ее как для создания переносимых (клиентских) приложений, так и для создания высокопроизводительных (серверных) систем, использующих всю специфику конкретной платформы и аппаратуры, сохраняя в то же время главное достоинство JAVA - современный объектно-ориентированный подход.