Java Native Interface

Technoblade

Erfahrenes Mitglied
Hallo alle miteinander.

Ich habe mir letztens mithilfe eines Buches C++ beigebracht. Da ich aus Gewohnheit allerdings immer noch meistens Java einsetze wollte ich mich jetzt auch an das Java Native Interface wagen. Bis zum kompilieren der cpp-Datei hat auch alles wunderbar funktioniert.
ich hatte die Frage auch schon im Java-Forum gestellt, dort konnte mir allerdings niemand wirklich weiter helfen. Zur Info, ich nutze cygwin, also den GNU-Compiler und Java Version 6 Update 14. Hier erst mal mein Quellcode:

Code:
#include <jni.h>
#include <stdlib.h>
#include <iostream>
#include "Test.h"

using namespace std;

JNIEXPORT void JNICALL 
Java_Test_testen(JNIEnv *env, jobject obj)
{
    cout << "Es klappt!" << endl;
}

Test.h ist automatisch von java erstellt. Verwende ich jetzt folgenden Befehl zum Kompelieren,

g++ -I /JavaPath/include/ -I /JavaPath/include/win32/ -shared Test.cpp -o Test.dll

dann spuckt der Cimpiler Meilen von Fehlermeldungen aus. Habe mir die mal in eine date schreiben lassen, dadurch kam ich dann schon mal auf die Idee auch noch den win32-Ordner einzubinden was das Errorlog von 64, auf 11 KB schrumpfen lassen hat.
Nur kompelieren lässt es sich leider dennoch nicht. Kann mir hier evtl. jemand helfen? Das Error-log befindet sich im Anhang, ist allerdings Denkbar unübersichtlich.

Ich hoffe, dass hier jemand einen Ansatz weiß, da ich auch noch nicht sehr erfahren in c++ bin.
 

Anhänge

  • err.txt
    10,4 KB · Aufrufe: 15
Hi

Der Header da scheint irgendwie Visual Studio zu bevorzugen...

Mach einmal vor dem ersten include das:
C++:
typedef long long int __int64;
Und bitte dann den neuen Errorlog reinstellen.
 
Viele Dank das hat mir schon ungemein geholfen. Jetzt sind schon mal alle Fehlermeldungen von g++ weg. Die Ausgabe bekomme ich leider dennoch nicht. Aber ich vermute, dass ich nur irgendwo nen kleinen Tippfehler oder so habe, sodass die Methode nicht richtig aufgerufen wird. Wenn ich dann nicht weiterkomme werde ich mich hier nochmals melden.

Aber was mich interessieren würde ist, das jetzt genau diese eine Zeile bewirkt hat. Fehlte dem Header nur dieser type?
 
Die Ausgabe bekomme ich leider dennoch nicht.
Gibt das Javaprogramm irgendwelche Fehlermeldungen, oder tut sich einfach nichts?
Zeig vielleicht auch mal den Javacode.

Aber was mich interessieren würde ist, das jetzt genau diese eine Zeile bewirkt hat. Fehlte dem Header nur dieser type?

Wenn man unter Visual Studio bzw. gcc "int" schreibt, bekommt man (auf einem 32bit-System) ein int, das 4 Byte hat.
Ein 8-Byte-int gibts auch, allerdings hat das in VS und gcc unterschiedliche Namen.
Bei VS nennt sich das __int64 (8 Byte = 64 bit), beim gcc long long int.

Die Headerdateien vom JNI brauchen solche 8-Byte-int oft und haben aber überall __int64 drin stehen.
Gut für Microsoft, schlecht für gcc.

Mit der Codezeile teilt man dem gcc jetzt einfach mit, dass __int64 wie long long int int zu behandeln ist.

Gruß
 
Es tut einfach nichts. Habe schon geprüft, die .dll-Datei wird gefunden. Nur die JVM scheint die passende Methode in der .dll-Datei nicht zu finden. Zumindest reagiert die JVM genau so wenn ich eine beliebige Methode einfach native mache zu der es keine wirkliche Umsetzung gibt.
Ich schlaf jetzt erst mal ne Nacht drüber. Vielen Dank für so viel Aufwand zu so später Stunde noch.

MfG Technoblade

EDIT: Bevor ichs noch vergesse, die java-Datei:

Code:
public class Test
{
    public Test() {
        System.loadLibrary("Test");
    }
    public native void testen();
    public static void main(String[] args) {
         new Test().testen();
    }
}
 
Zuletzt bearbeitet:
Habe jetzt nochmal ein wenig ausprobiert und auch nochmals alles neu kompiliert. Allerdings bleibt das Problem.
Ich erhalte weder beim ausführen der noch beim kompilieren Fehler.
Hatte das schonmal jemand, oder eine Idee woran es liegen könnte?
Oder erhalte ich ggf. keine Ausgabe, weil cout die Ausgabe irgendwo anders hin schreibt?
 
Um das cout zu testen, kannst du ja statt cout einmal ein Beep(1000,1000); machen (Ein-Sekunden-Ton).

Ich hab auf meiner Festplatte wieder ein kleines altes JNI-Programm gefunden; da lade ich die Library allerdings mit System.Load statt loadLibrary.

Probiers vielleicht einmal so:
C++:
System.load((new File("Test.dll")).getAbsolutePath());
 
Hi.

Das da nichts ausgegeben wird liegt an der Verwendung von Cygwin.

Wenn ich die DLL mit MinGWs GCC kompiliere erhalte ich:
Code:
$ java Test
Exception in thread "main" java.lang.UnsatisfiedLinkError: Test.testen()V
        at Test.testen(Native Method)
        at Test.main(Test.java:8)
Das wiederum liegt an deinem Code.

Das erste Problem ist, dass du die Funktion als C++ Funktion definiert hast. Aus "JNIEXPORT void JNICALL Java_Test_testen(JNIEnv *env, jobject obj)" wird dann in der DLL durch das C++ name mangling "_Z17_Java_Test_testenP7JNIEnv_P8_jobject@8". Deshalb kann Java die Funktion nicht finden.

Lösung: wie in der erzeugten Test.h die Funktion innerhalb von extern "C" { } definieren.

Das zweite Problem ist, dass Symbole (Funktionsnamen) bei verschiedenen Aufrufskonventionen unterschiedlich dekoriert werden.

Aus "JNIEXPORT void JNICALL Java_Test_testen(JNIEnv *env, jobject obj)" generiert der Microsoft Compiler in der DLL den Namen "_Java_Test_testen@8". Der GCC erzeugt keinen führenden Unterstrich: "Java_Test_testen@8".

Java erwartet allerdings den Namen "_Java_Test_testen@8" oder "Java_Test_testen" und kann die Funktion in der GCC DLL nicht finden.

Lösung: gib die Option -Wl,--add-stdcall-alias für den GCC an.

Das Programm funktioniert dann mit der MinGW DLL wie erwartet. Mit der Cygwin DLL gibt es Probleme (das Programm wird abgebrochen) - warum konnte ich jetzt nicht genau feststellen.

Gruß
 
Hab mich jetzt wo ich etwas mehr zeit habe nochmals dran gesetzt und mit MinGw probiert. Funktioniert jetzt dank eurer Hilfe alles, vielen Dank.
 
Zurück