# JNI und C++



## fett32 (22. Jun 2010)

Hallo,

also ich hab hier folgende Situation. Eine GUI erstellt in Java und ein 10.000 Zeilen Programm erstellt in C++. Die GUI soll nun eine Methode aus dem c++ Programm aufrufen, welche einen String an eine Methode in der GUI liefert. Als ich dann bisschen darüber gelesen hab, wie man Java mit C++ verbindet, bin ich auf dieses JNI gestoßen. 
Aber iwie finde ich keine guten Infos wie man vorgehen soll, wenn schon ein großes C++ Programm existiert.

Könnt ihr mir vll. ein paar Tipps oder Links geben?

grüße


----------



## Tomate_Salat (22. Jun 2010)

c++ wrapper-dll schreiben, die die Methoden deines Programms ausführt


----------



## Siassei (22. Jun 2010)

Servus,

step by step 

Ich würde dir empfehlen, dich erstmal in die Technologie (Java <-> Native) einzuarbeiten und danach für einen konkreten Anwendungsfall etwas proggen.

Java Native Access ? Wikipedia bietet eine gute Alternative. Vielleicht ist da was für dich mit dabei.
JDK 6 Java Native Interface-related APIs & Developer Guides -- from Sun Microsystems eine sehr gute Seite mit Beispielen.
Galileo Computing :: Java ist auch eine Insel (8. Auflage) – 27 Java Native Interface (JNI)


----------



## fett32 (22. Jun 2010)

Moin,

danke schon mal für die schnellen Antworten, werd mir die Links durchlesen...nebenbei bin ich gerade noch durch Tomate_Salat noch auf das tool cxxwrap gestoßen. Hat den schon mal jemand genutzt?


----------



## Siassei (22. Jun 2010)

Siehe letzter Link Kapitel 27.5

Warum probierst du es nicht einfach mal?  Zieh ein kleine Testprojekt auf und die dabei entstehenden Fragen kannst du hier mit Code posten. Die meisten Leute helfen dir dann auch gerne.
Auf deine aktuellen Fragen kann man keine vernünftige Antwort geben, da sie zu allgemein sind.

Das beste Verständnis wirst du wohl bekommen, wenn du mit JNI und
JDK 6 Java Native Interface-related APIs & Developer Guides -- from Sun Microsystems
ein Beispielprojekt erstellst.
Zudem solltest du dir gedanken machen, ob die GUI das C++-Programm startet oder das C++Programm die GUI starten soll. siehe
http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/invocation.html#wp9502


----------



## fett32 (23. Jun 2010)

Jope werd jetzt mal mit einem kleinen Projekt anfangen. Schnell iwelche ergebnisse erzielen zu wollen war wohl zu naiv. 

Also die Sache soll bis jetzt so ablaufen. Ich hab ne GUI die ruft das c++ Programm auf, indem es eine txt-datei übergibt. Diese datei wird dann von dem c++ Programm benutzt um gewisse rechnungen anzustellen und um anschließend das Ergebnis zur Visualisierung an die GUI zurück zu geben. 

Prizipiell brauch ich also "nur" in dem Programm die Methode die die txt datei übergeben bekommt in die GUI einbinden und die Mehtode die das Ergebnis liefert.

Aber wie gesagt, ich werd jetzt erstmal von ganz vorne anfangen, brute forcen macht wenig sinn


----------



## Tomate_Salat (23. Jun 2010)

....nein. Du hast den Sinn von JNI nicht verstanden.Du rufst über java eine *.dll datei auf und übergibst dort ganz normal die Parameter an die C++ (oder in was auch immer diese dll geschrieben wurde) funktion. Das schnelle schema sieht so aus:

- Du hast eine Java-Klasse, dort lädst du die Library und definierst deine nativen Methoden.Bsp:

```
public native String sendSomethingToDLLAndReceiveAString(String data);
```
- Dann lässt du dir über [c]javah -jni {DEINE_KLASSE}[/c] eine header-Datei erstellen
- Diese headerdatei verwendest du in einem, hier, c++-projekt und deklarierst die Funktionen, danach erstellt du daraus eine dll
- Wenn du in Java die dll über System.loadLibrary("mydll"); aufrust, legst du die dll ins "Root"-Verzeichniss deiner anwendung

Danach kannst du in Javah die native Methode sendSomethingTo... ganz normal aufrufen und sie wird dann über die dll ausgeführt.

MFG

Tomate_Salat


----------



## fett32 (29. Jun 2010)

So jetzt kommt mal ein erster Versuch, jetzt wo ich wieder Zeit hab.

Ich hab meine maximal simple Java datei:


```
class HelloWorld {
//Native method declaration
  public static native int add(int a, int b);
//Load the library
  static {
    System.loadLibrary("nativelib");
  }

  public static void main(String args[]) {

    int c;
    c = add(4,5);
    }
  }
```

darauf hab ich dann javac HelloWorld.java und javah -jni -HelloWorld ausgeführt

anschließend hab ich folgende c datei geschrieben


```
#include "HelloWorld.h" 


JNIEXPORT int JNICALL Java_HelloWorld_add(JNIEnv *env, jclass cl, int a, int b) 
{ 
  
  int k = a + b;
  return k;
}
```

soweit so gut, aber wenn ich jetzt wie auf Chapter 5 Continued: JNI Example angegeben 

gcc  -o libnativelib.so -shared -Wl,-soname,libnative.so  
     -I/export/home/jdk1.2/include 
     -I/export/home/jdk1.2/include/linux World.c  
     -static -lc

ausführe bekomme ich :

/usr/lib64/gcc/x86_64-suse-linux/4.4/../../../../x86_64-suse-linux/bin/ld: /usr/lib64/gcc/x86_64-suse-linux/4.4/crtbeginT.o: relocation R_X86_64_32 against `__DTOR_END__' can not be used when making a shared object; recompile with -fPIC
/usr/lib64/gcc/x86_64-suse-linux/4.4/crtbeginT.o: could not read symbols: Bad value
collect2: ld returned 1 exit status

als fehlermeldung...leider kann ich auch mit der Fehlermeldung nicht so viel anfangen.

Also eigentlich hab ich jeden schritt auf der seite nacheinander ausgeführt so wie er beschrieben war, allerdings mit eigenem code. Vll. liegt da iwo der Fehler, ich sehe ihn momentan auf jeden fall noch nicht.


----------



## Tomate_Salat (29. Jun 2010)

das int ist hier falsch, mach ein jint (müsste es sein) draus. Das müsste aber auch in der header so angezeigt werden.
(Das kannst du glaub ich aber genauso behandeln wie ein int)


----------



## fett32 (2. Jul 2010)

okay hab ich geändert und bin auch weitergekommen, allerdings nur zur nächsten Fehlermeldung :/ ...

meine c datei sieht jetzt so aus


```
#include <jni.h>
#include "HelloWorld.h" 


JNIEXPORT jint JNICALL Java_HelloWorld_add(JNIEnv * env, jclass cl, jint a, jint b) 
{ 
  int k;
  k = a + b;
  return k;
}
```

wieder wie auf der Website eingetippt mit folgender Meldung diesmal:

/usr/lib64/gcc/x86_64-suse-linux/4.4/../../../../lib64/crt1.o: In function `_start':
/usr/src/packages/BUILD/glibc-2.10.1/csu/../sysdeps/x86_64/elf/start.S:109: undefined reference to `main'
collect2: ld returned 1 exit status


----------



## Siassei (2. Jul 2010)

Servus,

du gibts immer noch ein int zurück. Das sollte doch eher ein jint sein.
Die zweite Meldung deutet auf eine fehlende main-Methode hin. Mit welcher Anweisung hast du das ganze kompiliert?


----------



## fett32 (2. Jul 2010)

ähm du meinst int k; zu jint k; machen?

ansonsten hab ich folgenden Befehl benutzt 

gcc  -o libnativelib.so -shared -Wl,-soname,libnative.so  
       -I/.../include 
       -I/.../include/linux World.c  
       -static -lc

"..." steht hier für meinen Pfad 
quasi nur von der website kopiert und bisschen angepasst.


----------



## Siassei (3. Jul 2010)

fett32 hat gesagt.:


> ähm du meinst int k; zu jint k; machen?


Ja, aber es macht eigentlich keinen Unterschied 

Ich sehe bei dir keine Anweisung für 32bit. Besitzt du eine 64bit JVM?
Füge doch mal das Kommando "-m32" hinzu, um in 32bit zu compilieren.

Ansonsten wäre ich da überfragt. Liegt deine Klasse in einem Paket (package) oder nicht?
Wie sieht dein Header aus?


----------



## fett32 (8. Jul 2010)

Moin,

so mal wieder Zeit daran zu arbeiten 

Die Klasse liegt in keinem Packet, -m32 hat nicht geholfen , und der header sieht so aus


```
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloWorld
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_HelloWorld_add
  (JNIEnv *, jclass, jint, jint);

#ifdef __cplusplus
}
#endif
#endif
```

Kann denn mal jemand das Bsp. bei sich ausprobieren? Damit ich ausschließen kann, dass es an meinem System liegt


----------



## Marco13 (8. Jul 2010)

Das ist schon sehr ähnlich zu http://www.java-forum.org/allgemeine-java-themen/102689-jni-problem.html - versuch vielleicht nochmal den aktuellen Stand und das genaue Problem zusammenzufassen...


----------



## fett32 (8. Jul 2010)

Moin, also ich denke jetzt, dass es an meinem System liegt. Denn ich hab, warum bin ich da nicht eher drauf gekommen, einfach vollständig das Bsp. von der Seite kopiert und dabei kommt genau der gleiche Fehler, also kanns schon mal nicht an meinem Code liegen.

Ansonsten treffen Beitrag #10 und #12 genau das problem


----------



## fett32 (8. Jul 2010)

Bin einen Schritt weiter

gcc -I/usr/.../include -I/usr/.../include/linux -shared -Wl,--add -o HelloWorld.so HelloWorld.c

produziert keine Fehler, dafür aber jetzt das ausführen
also java HelloWorld

Exception in thread "main" java.lang.UnsatisfiedLinkError: no HelloWorld in java.library.path
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1734)
        at java.lang.Runtime.loadLibrary0(Runtime.java:823)
        at java.lang.System.loadLibrary(System.java:1028)
        at HelloWorld.<clinit>(HelloWorld.java:6)
Could not find the main class: HelloWorld.  Program will exit.


okay da steht zwar no HelloWorld in java.library.path, aber eigentlich müsste das funktionieren.

Hab zuvor wie auf der Website 

  LD_LIBRARY_PATH=`pwd`
  export LD_LIBRARY_PATH

gemacht


----------



## Marco13 (8. Jul 2010)

Wenn die HelloWorld.DLL in dem Verzeichnis liegt, von dem aus du das Programm startest, sollte er sie finden. Ansonsten kannst du beim Start mit
java -Djava.library.path=DER_PFAD DasProgramm
den Pfad angeben.

EDIT: Ach, DLL, quatsch, hier gehts ja um Linux... also die libHelloWorld.so...


----------



## fett32 (8. Jul 2010)

ist im gleichen Verzeichnis


----------



## Marco13 (8. Jul 2010)

Beachte, dass sie "*lib*HelloWorld.so" heißen muss, bei System.loadLibrary aber dann trotzdem nur "HelloWorld" steht (den Rest macht Java alleine...)


----------



## fett32 (8. Jul 2010)

okay,

also mein Befehl ist dann nun

gcc -I/usr/lib64/jvm/java-1.6.0-sun-1.6.0/include -I/usr/lib64/jvm/java-1.6.0-sun-1.6.0/include/linux -shared -Wl,--add -o libHelloWorld.so HelloWorld.c

produziert keinen Fehler, aber der Java aufruft liefert noch die gleiche Meldung.

Nebenbei, ist das überhaupt der richtige Befehl? Hatte den aus einem anderen Forum kopiert und bisschen reduziert weil ers so nicht getan hat. Das original war:

gcc -IC:\...\jdk1.6.0_03\include\ -IC:\...\jdk1.6.0_03\include\win32\ -shared -Wl,--add-stdcall-alias -o Name.dll Name.c

für windows allerdings.


----------



## Marco13 (8. Jul 2010)

Also das "-Wl,--add" macht so wohl keinen Sinn ... "ein bißchen reduziert" ist lustig. Wenn dein Programm nicht compiliert, nimmst du ja auch nicht "ein bißchen was von dem Programm" weg. Aber das
-Wl,--add-stdcall-alias
sollte meines (Halb)wissens nach unter Linux nicht nötig sein, d.h. das sollte man AFAIK auch GANZ weglassen können.


----------



## fett32 (12. Jul 2010)

mhm okay so wäre ich aber dann wieder bei dem alten fehler

/usr/lib64/gcc/x86_64-suse-linux/4.4/../../../../lib64/crt1.o: In function `_start':
/usr/src/packages/BUILD/glibc-2.10.1/csu/../sysdeps/x86_64/elf/start.S:109: undefined reference to `main'
collect2: ld returned 1 exit status


wie gesagt, das kommt auch, wenn ich das Bsp. von der Seite genau kopiere

PS: Meine Bitte steht immer noch, ob jemand mit einem Linux system auch mal das bsp testen könnte 

edit:

gerade noch hier im forum gefunden

gcc -fPIC -g -c -Wall strlen.c -I/usr/lib64/gcc/x86_64-suse-linux/4.3/include/
gcc -shared -Wl,-soname,mylib.so -o mylib.so strlen.o

damit kommt allerdings dann folgende meldung ...aahhh warum kann das nicht mal einfach laufen

/usr/lib64/jvm/java-1.6.0-sun-1.6.0/include/jni.h:39: error: conflicting types for ‘jboolean’
/usr/lib64/gcc/x86_64-suse-linux/4.4/include/jni_md.h:81: note: previous declaration of ‘jboolean’ was here

warum auch immer das kommt


----------



## Marco13 (12. Jul 2010)

Bei
gcc -shared -o libName.so -IC:\...\jdk1.6.0_03\include\ -IC:\...\jdk1.6.0_03\include\win32\ Name.c
sollte er eigentlich keine main finden wollen, weil dort eine shared library erstellt wird.


----------



## fett32 (12. Jul 2010)

problem behoben

eeeeeendlich

g++ -shared -fPIC -O3 -I/usr/lib/jvm/java-6-sun-1.6.0.20/include/ -I/usr/lib/jvm/java-6-sun-1.6.0.20/include/linux/ jni_test.cpp -o libjni_test.so

java -Djava.library.path=. JNITests

plus java-1.6.0-openjdk ordner haben das problem gelöst. Mit java-6-sun-1.6.0.20 geht allerdings immer noch nicht...naja egal wenns jetzt läuft


----------



## Siassei (12. Jul 2010)

Kann es sein, dass deine JVM von SUN eine 32bit Version ist und die OpenJDK 64bit?


----------



## fett32 (19. Jul 2010)

nee war beides 64 bit


----------

