JNI und C++

fett32

Mitglied
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
 

Siassei

Bekanntes Mitglied

fett32

Mitglied
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

Bekanntes Mitglied
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

Mitglied
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 :)
 
T

Tomate_Salat

Gast
....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:
Java:
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

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

Ich hab meine maximal simple Java datei:

Java:
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

Java:
#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.
 
T

Tomate_Salat

Gast
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

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

meine c datei sieht jetzt so aus

Java:
#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

Bekanntes Mitglied
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

Mitglied
ä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

Bekanntes Mitglied
ähm du meinst int k; zu jint k; machen?
Ja, aber es macht eigentlich keinen Unterschied :D

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

Mitglied
Moin,

so mal wieder Zeit daran zu arbeiten :)

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

Java:
/* 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 :)
 

fett32

Mitglied
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
 
Zuletzt bearbeitet:

fett32

Mitglied
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

Top Contributor
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...
 

Marco13

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

fett32

Mitglied
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

Top Contributor
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

Mitglied
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 :D...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
 
Zuletzt bearbeitet:

Marco13

Top Contributor
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

Mitglied
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
 

Neue Themen


Oben