Вызов функции dll java

Как подружить Java и C++. Часть первая

Как вы, наверное, уже догадались, речь пойдет о JNI. Для тех, кто не знает что это, объясняю: JNI (или java native interface) — это такая штука, которая позволяет делать вызовы нативного кода из java машины и наоборот.

Зачем это может потребоваться? Есть несколько причин: необходимость использовать код, который уже написан для нативной платформы, необходимость реализовать что-то такое, что невозможно сделать с помощью одной JVM (например, работа с какими-нибудь специфическими железками), ну и ускорение выполнения критических кусков кода (правда, это весьма спорный момент).

Как работает JNI

Допустим, у нас есть какой-то java класс из которого надо вызвать метод, написанный на c++ и находящийся в динамически связываемой библиотеке (например, в windows это будет dll). Что мы должны для этого сделать?

Для начала мы объявляем метод какого-нибудь класса как native. Это будет означать, что JVM при вызове этого метода будет передавать управление нативному коду.

Затем, нам надо загрузить нативную библиотеку. Для этого можно вызвать System.loadLibrary(String) , которая принимает в качестве параметра имя библиотеки. После этого вызова библиотека будет загружена в адресное пространство JVM.

Теперь, представим, что у нас есть следующий java класс:

public class NativeCallsClass
<
static
<
System . loadLibrary ( «megalib» ) ;
>

native public static void printOne ( ) ;
native public static void printTwo ( ) ;
>

Здесь мы, для удобства, вынесли loadLibrary() в static область класса.

Допустим, теперь, что мы вызываем NativeCallsClass.printOne() . Тогда JVM будет искать в библиотеках метод со следующим именем: Java_my_mega_pack_NativeCallsClass_printOne(. ) .

Объявление JNI функций в C++

Мы написали класс на java, у которого есть методы, помеченные как native. Теперь нам надо создать хедеры с объявлениями функций C++, которые мы хотим вызывать.

Конечно, можно написать их вручную. Но есть более удобный метод:

javac -d bin/ src/my/mega/pack/NativeCallsClass.java cd bin javah my.mega.pack.NativeCallsClass

Мы компилируем класс, а потом используем утилиту javah. После этого у нас появится файл, который называется my_mega_pack_NativeCallsClass.h. Это и есть наш хедер. Выглядит он примерно так:

/* DO NOT EDIT THIS FILE — it is machine generated */
#include
/* Header for class my_mega_pack_NativeCallsClass */

#ifndef _Included_my_mega_pack_NativeCallsClass
#define _Included_my_mega_pack_NativeCallsClass
#ifdef __cplusplus
extern «C» <
#endif
/*
* Class: my_mega_pack_NativeCallsClass
* Method: printOne
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_printOne
( JNIEnv * , jclass ) ;

/*
* Class: my_mega_pack_NativeCallsClass
* Method: printTwo
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_printTwo
( JNIEnv * , jclass ) ;

Самое главное здесь — это сигнатуры 2х функций: Java_my_mega_pack_NativeCallsClass_printOne(JNIEnv *env, jclass myclass) и Java_my_mega_pack_NativeCallsClass_printTwo(JNIEnv *env, jclass myclass) .

Их-то нам и надо реализовать. Для начала разберемся с их сигнатурами. env — это интерфейс к виртуальной машине. Все операции с JVM выполняются с помощью него. Позже мы разберем это подробнее. myclass — это идентификатор java класса, у которого есть метод native, отождествленный с этой функцией, то есть в нашем случае это NativeCallsClass . Обратите внимание, что jclass в качестве второго параметра передается тогда, когда метод объявлен как static. Если бы он был обычным методом, то нам бы передавался jobject, который бы идентифицировал объект, метод которого мы вызвали (фактически это аналог this).

Нам остается только реализовать эти функции:

JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_printOne ( JNIEnv * env, jclass myclass )
<
std :: cout >

JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_printTwo ( JNIEnv * env, jclass myclass )
<
std :: cout >

Передаем данные в нативный код и обратно

Давайте теперь реализуем более сложное поведение. Пусть у нас будет 2 метода: inputInt и outputInt. Один из них будет считывать число с консоли, а второй — выводить. Наш java класс будет выглядеть так:

public class NativeCallsClass
<
static
<
System . loadLibrary ( «megalib» ) ;
>

native public static int inputInt ( ) ;
native public static void outputInt ( int v ) ;
>

JNICALL Java_my_mega_pack_NativeCallsClass_inputInt ( JNIEnv * , jclass ) ;
JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_outputInt ( JNIEnv * , jclass, jint ) ;

jint — это typedef. Фактически он обозначает некоторый примитивный тип (например, int), который соответствует int в java. Как видим, задача оказалась не на много сложнее предыдущей 🙂 Наши функции будут выглядеть так:

JNIEXPORT jint JNICALL Java_my_mega_pack_NativeCallsClass_inputInt ( JNIEnv * env, jclass myclass )
<
int ret;

JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_outputInt ( JNIEnv * env, jclass myclass, jint v )
<
std :: cout >

Подводим итог

Итак, в первой части мы рассмотрели, как работает JNI, как писать java классы, с помощью которых можно осуществлять вызовы нативного когда и как писать C++ функции, вызываемые через JNI. В следующей части (или частях) мы рассмотрим взаимодействие с JVM из C++ кода, работу с классами, объектами полями и методами, создание proxy классов java, которые бы представляли C++ классы и запуск JVM из C++ кода.

Естественно, продолжение будет только в том случае, если это кому-то интересно 🙂

Источник

C# to Java (вызов C# dll из Java)

Доброго времени суток!
Помучив немного хабра поиск не нашел подобных тем, в связи с чем создал свою.

Немного лирики:
Имеем шарповскую библиотеку, в которой лежат необходимые для работы методы. Необходимо этими методами воспользоваться из Java программы.

Пример первый

Сначала покажу простой пример.
Имеем примитивную dll на шарпе, назовем ее SharpClass:

public class CSharpHelloWorld < public CSharpHelloWorld() < >public void displayHelloWorld() < Console.WriteLine("Hello World From C#!"); >> 

Будем собирать этот класс в сборку:
Открываем командную строку, и прописываем такую команду (предварительно нужно зайти в директорию с нашим SharpClass.cs файлом): csc \t: module SharpClass.cs. Если команда не работает, то перед ее запуском нужно выполнить бат-файл, у меня он находится здесь — C:\Program Files\Microsoft Visual Studio 9\VC\bin\vcvars32.bat
После выполнения сей процедуры мы получим файл SharpClass.netmodule, находящийся в одной папке с исходником.

Для связи между .NET и JVM нужен враппер (обертка для получения unmanaged кода). Создадим обертку из с++ с использованием явовской библиотеки jni.h и майкрософтовской библиотеки mscorlib.dll
Создаем с++ обертку:

#using #using "SharpClass.netmodule" using namespace System; __gc class HelloWorldC < public: CSharpHelloWorld __gc *t; HelloWorldC() < t = new CSharpHelloWorld(); >void method() < t ->displayHelloWorld(); > >; 

После написания этого кода необходимо указать ссылку на mscorlib.dll. Для этого заходим в настройки проекта и сначала находим General, где в пункте Common Language Runtime Support выбираем Common Language Runtime Support, Old Syntax (/clr:oldSyntax), после этого жмем References (ссылки) и добавляем новую — во вкладке .NET будет искомая dll (так было в VS2005).

#include "C:\Program Files\Java\jdk1.6.0_02\include\jni.h" #include "HelloWorld.h" #include "1.cpp" JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject) < HelloWorldC* t = new HelloWorldC(); t->method(); > 
JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject); 

И последний хедер генерируется при помощи Java из командной строки запуском команды — javah -jni «имя класса без кавычек и без .class» (создавать его естественно нужно после компиляции ява программы, иначе сам класс просто не появится). В файлах 1.h и 2.cpp код также взят из последнего хедера.

#ifndef _Included_HelloWorld #define _Included_HelloWorld #ifdef __cplusplus extern "C" < #endif /* * Class: HelloWorld * Method: displayHelloWorld * Signature: ()V */ JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject); #ifdef __cplusplus >#endif #endif 

После этого весь c++ проект строится в dll. Назовем его HelloWorld.dll.

Теперь переходим к Яве. Вот простой код для вызова нашей HelloWorld.dll:

import java.io.*; class HelloWorld < public native void displayHelloWorld(); static < System.load("C:\\Java_C#\\JavaApplication1\\HelloWorld.dll"); >public static void main(String[] args) < new HelloWorld().displayHelloWorld(); >> 

Вот собственно все готово. Ну и наш SharpClass.netmodule также необходимо перенести в папку с загружаемой сишной dll.

Пример второй

В первом примере рассматривался простой случай просто с выводом текста на экран. Сейчас рассмотрим случай, когда функция на C# принимает параметры и возвращает значение.
C# класс:

using System; using System.Collections.Generic; using System.Text; public class CSharpToJava < public CSharpToJava() < >public String returnValue(String value) < string ss = " cvb"; String answer = value+ss; return answer; >> 

Java приложение, использующее этот класс:

public class Main < public native String returnValue(String value); static < System.load("C:\\Java\\SharpToJava\\CSharpToJava.dll"); >public static void main(String[] args) < String value="Privet"; String val = new Main().returnValue(value); System.out.println(val); >> 

После этого создаем хедер для с++ с помощью команды, описанной в первом примере.
Далее создаем с++ обертку, которая таскает переменные из одного конца в другой :). Будет 3 файла — wrapper.cpp, CSharpToJava.cpp, Main.h (последний получается выполнением команды javah -jni над явовским классом)

#include #include #using //Подключение майкрософтовской библиотеки для создания native кода #using "CSharpClass.netmodule" //Подключение шарповской сборки, в который содержится вызываемая из явы функция using namespace std; using namespace System; //Создаем класс-обертку для преобразования шарповской функции в native код __gc class SendValue < public: CSharpToJava __gc *t; //CSharpToJava - класс шарпа. //t - указатель на него из с++ SendValue() < t = new CSharpToJava(); >String __gc* method(String __gc* value) < return (t ->returnValue(value)); > >; 
#include "C:\Program Files\Java\jdk1.6.0_02\include\jni.h" #include "Main.h" #include "wrapper.cpp" #include #include using namespace System::Runtime::InteropServices; //нужен для использования Marshal class, который делает перевод из String* в const Char* using namespace std; //Main функция //Экспортируемая функция из явы //Заголовок получается путем выполнения команды javah -jni "имя класса, пишется без кавычек и .class" JNIEXPORT jstring JNICALL Java_Main_returnValue (JNIEnv* env, jobject, jstring jvalue) < //Получение переменной jvalue, передаваемой из явы и присвоение сишной переменной value с типом String __gc* String __gc* value=env->GetStringUTFChars(jvalue,0); //Получение указателя на объект класса SendValue, который описан в wrapper.cpp SendValue* t = new SendValue(); //Получение значения из шарповской функции и присвоение его переменной val типа String __gc* String __gc* val = t->method(value); //Преобразование типа String* в const char* char* str2 = (char*)(void*)Marshal::StringToHGlobalAnsi(val); jstring jval; //Преобразование типа const char* в jstring, который требует вернуть функция явы jval=env->NewStringUTF(str2); //Возвращение явовской переменной return jval; > 
/* DO NOT EDIT THIS FILE - it is machine generated */ /* Header for class Main */ #ifndef _Included_Main #define _Included_Main #ifdef __cplusplus extern "C" < #endif /* * Class: Main * Method: returnValue * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_Main_returnValue (JNIEnv *, jobject, jstring); #ifdef __cplusplus >#endif #endif 

Заключение

Ну вот собственно и все. Данный код писал и испытывал 2 года назад, а сюда все никак руки не доходили написать. Когда разбирался с этой задачей, пытался просить помощи на одном форуме. Пока помощи просил, сам немного разобрался и написал свое решение.

Полезные ссылки

Источник

Читайте также:  Java sql date and java util date
Оцените статью