Pull to refresh

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

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

Немного лирики:
Имеем шарповскую библиотеку, в которой лежат необходимые для работы методы. Необходимо этими методами воспользоваться из 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
Создаем с++ обертку:

1.cpp
#using <mscorlib.dll>
#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).

2.cpp
#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();
}


1.h
JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld
 (JNIEnv *, jobject);


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

HelloWorld.h
#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 над явовским классом)

wrapper.cpp
#include #include <string>
#using <mscorlib.dll>      //Подключение майкрософтовской библиотеки для создания 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));
  }
};


CSharpToJava.cpp
#include "C:\Program Files\Java\jdk1.6.0_02\include\jni.h"
#include "Main.h"
#include "wrapper.cpp"
#include #include <string>
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;
}


Main.h
/* 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 года назад, а сюда все никак руки не доходили написать. Когда разбирался с этой задачей, пытался просить помощи на одном форуме. Пока помощи просил, сам немного разобрался и написал свое решение.

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


Tags:
Hubs:
Total votes 14: ↑11 and ↓3 +8
Views 17K
Comments Comments 11