JNI - Erste Versuche

Hey,

ich versuche mich erstmals daran selber das JNI zu benutzen mit einer eigenen C-Datei. Ich benutze dafür folgendes Tutorial:
http://www.die-seite.ch/?jnitutorial

Das Problem ist nun, das ich Netbeans und nicht eclipse benutze und mir so einiges selber zusammenreimen muss, was bei Projekt 2 Schritt 2 nicht mehr funktioniert. Ich hab bis dahin alles gemacht, aber wenn ich den Inhalt in meine angelegte C-Datei kopiere schaut das ganze so aus:

Ich bekomme also einige Fehler und weiß nich warum… Ich denke das problem liegt bei den Prjekteinstellungen die ich ja nicht 1 : 1 übernehmen kann. Das was er in “Include Paths” eingefügt hat, habe ich unter “Build/C Compiler/Inlcude DIrectories” rein gemacht. Bei dem Linker zeug hab ich allerdings ka gehabt worein… Habs danne infach bei “Build/Linker/Compilation Line Additional Options” rein. Die eine Zeile habe ich auch ersetzt,

Dann sollte ich ja eine main.c erstellen und das rein kopieren. Habe ich gemacht und das Ergebnis seht ihr oben. Was hab ich falsch gemacht?

Edit: Hab gerade versucht die ganzen Header Dateien in “Include HEaders” alle einzeln nochmal zusätzlich hinzuzufügen, klappt auch nich. In der Header-Datei Kommen die selben Fehler, ich denk mal das ist im Zusammenhang damit, das er die jni.h Datei nicht findet, obwohl ich diese in diesem Schritt manuell in inluce headers hinzugefügt habe. Theoretisch könnte ich diese noch alle im Projekt ansich hinzufügen, das sollte aber nich die Lösung sein das ich da alle ins Projekt klatsch.

Als ich das letzte mal mit jni zu tun hatte, musste man das komplette Verzeichnis samt Unterverzeichnisse, in welchem sich die jni.h befindet, dem Include-Pfad hinzufügen, weil sie ihrerseits auf Dateien darin zugreift, z.B. auf die jni_md.h im definierten arch-Verzeichnis wie etwa win32.

Da ich nie echt mit Netbeans gearbeitet habe, und schon gar nicht mit C, bin ich nicht sicher: Geht es nur um das “unterstrichene”? Kann es sein, dass der das für eine Java-Datei hält? (Ganz naiv gefragt…)

@Marco: Nein, ich hab das ganze Als Dynamische C-Library angelegt und die Fehler kommen alle, weil der die jni.h nicht findet.

@anon19643277:Exat das hab ich ja gemacht und das Ergebnis siehst du oben.

Habe Zumindest die Fehler in meinen Dateien wegbekommen indem ich jetz einfach mal stupide ALLE .h Dateien aus dem Java Ordner in mein Projektordner nochmal kopiert habe und dann dem Projekt hinzugefügt, verdammt unschön und auch nicht Sinn der Sache aber die Fehler sind weg. Gibt aber weitere Probleme:

  1. Lasse ich die Bridge-Dateien weg bekomme ich folgende Meldung wenn ich die dll erzeugen will:
    http://puu.sh/vjJ0O/91e9a7d1c7.png
  2. Füge ich diese hinzu, bekomm ich nen Compilation Error:
    http://puu.sh/vjJ2P/f28b68fd79.png
    Er findet AccessBridgeDebug.h nicht, die gibts ja auch nicht, weder im java Ordner noch sonstwo…

Es ist doch zum verzweifeln :dizzy_face:

Moin,

persönliches: ich finde es immer komisch, wenn jemand Cygwin verwendet und man für Windows etwas kompilieren will (Windows → Hybrid-Linux → Windows-DLL). Gerade wo MS eine gute IDE kostenlos anbietet.

Entsprechend meiner persönlichen Meinung empfehle ich Dir das Du Dir erstmal von MS Visual Studio installierst und dort die DLL baust. Ob Du nun unter Cygwin mit Netbeans versucht die Einstellungen hin zu bekommen oder mit Visual Studio ist irgend wo gleich. Da Du erstmal nur für Windows kompilierst, brauchst Du Dich nicht mit Cross-Plattform rumärgern - wofür der GCC nun mal gedacht ist.

öhm - nö. Erfindet eine „AccessBrideDebug.h“ nicht und bricht damit den Compilevorgang ab (letztes Bild). Die „jni.h“ hat er da schon gefressen.

Wenn ich mir im letzten Bild den GCC-Aufruf anschaue, dann rollen sich mir die Zehnägel. -I../../../../../Programm\File/java/include/win32. Das ist kein absoluter Pfad, so etwas macht man nicht. An der Stelle löst der GCC nämlich den aktuellen Pfad falsch auf, wenn er nicht im richtigen Verzeichnis gestartet ist. Was anscheinend das Hauptproblem ist.

Ja und Nein. Es ist möglich das er die Calls bzw. Defines einfach nur nicht erkennt. Macht Eclipse genau so. Das ist dann ein Hinweis, das das Projekt nicht richtig konfiguriert ist. Wobei man bei Eclipse zwischen durch auch mal den Indexer neu an schmeißen muss (damit er da wieder mehr Sachen erkennt)

@mogel: Danke erstmal für den ausführlichen Kommentar!

  1. Ich brauch gcc aber, da wir in der Schule Linux benutzen und ich Zuhause Windows. Ich möchte das hier aber erstmal hinkriegen und es in der Schule dann wenn möglich fast genauso machen können, daher versuche ich es gleich mit dem GCC. Visual Studio hab ich sogar als Offizielle Version von der Schule bekommen, aber ich wollte solange darauf verzichten bis wir mit C# anfangen, da das Netbeans nicht unterstützt.

  2. Doch das tun sie. Der Fehler mit der Debug-Datei kommt ja auch erst wenn ich ALLE Header-Dateien aus dem Include ordner per Hand ins Projekt kopiere und sie dann hinzufüge, dann MUSS er die jni.h ja finden - was er dann auch tut - Die Fehler verschwinden. Die Debug-Datei kommt ja auch erst beim kompilieren zum Zuge, da diese in den Headerdateien wiederum aufgerufen wird. Lasse ich die Headerdateien im Include-Path ohne sie hinzuzufügen kommen die ganzen Fehler wieder. Die Debug-Datei existiert schlicht und ergreifend bei mir nicht. Kann ja evtl von euch in seinem Ordner mal schauen ob die vorhanden ist, bei mir ist es nicht der Fall.

  3. Ich habe die Pfade aber eigentlich mit dem normalen Öffnen-Dialog von Netbeans aufgemacht und hinzugefügt… ICh denke das sie einfach verkürzt dargestellt werden. So schauen die beiden aus:
    http://puu.sh/vjPKX/ea74eaac0c.png
    http://puu.sh/vjPMw/220def399b.png

Hm, ich probier noch weiter bissl rum

Hmmm,

als jede einzelne Header brauchst Du nicht hinzufügen. Es reicht das Root-Verzeichnis. Außerdem hast Du eine C-Datei als Include-Datei hinzugefügt. Nimm die mal raus (zweites Bild, dritte Zeile).

NetBeans selber scheint das Ganze schon in releative Pfad zu wandeln - ist irgend wie Mist. Kannst Du mal bitte Dein Projekt zippen und hoch laden. Ich schau dann mal bei Gelegenheit auch drüber.

Natürlich. Hier bitteschön:
JniDLL.zip (66,1 KB)

Sooooo,

ich habe jetzt meine Windows-VM mit NetBeans und Cygwin gefoltert.

Beim Hinzufügen der Include-Verzeichnisse, kannst Du zwischen absolut und relative wählen. Wähle bitte absolut, dann brauchst Du auch die ganzen einzelnen Dateien nicht hinzufügen. Also nur “include Directorys” und nicht “Include Headers”.

  • "C:/Program Files/Java/jdk1.8.0_101/include"
  • "C:/Program Files/Java/jdk1.8.0_101/include/win32"

Anschließend findet er PrintDebugString nicht. Ursprünglich dachte ich da an eine WinAPI Funktion, die heißt aber OutputDebugString. Scheint also wieder etwas von JNI zu sein.

mein Compile Vorgang:

cd 'C:\Users\mogel\Documents\NetBeansProjects\JniDLL'
C:\cygwin64\bin\make.exe -f Makefile CONF=Debug
"/usr/bin/make" -f nbproject/Makefile-Debug.mk QMAKE= SUBPROJECTS= .build-conf
make[1]: Entering directory '/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL'
"/usr/bin/make"  -f nbproject/Makefile-Debug.mk dist/Debug/Cygwin-Windows/libJniDLL.dll
make[2]: Entering directory '/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL'
mkdir -p dist/Debug/Cygwin-Windows
gcc     -o dist/Debug/Cygwin-Windows/libJniDLL.dll build/Debug/Cygwin-Windows/AccessBridgeCalls.o  -shared
build/Debug/Cygwin-Windows/AccessBridgeCalls.o: In function `initializeAccessBridge':
/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL/AccessBridgeCalls.c:74: undefined reference to `PrintDebugString'
/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL/AccessBridgeCalls.c:74:(.text+0x47): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `PrintDebugString'
/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL/AccessBridgeCalls.c:74: undefined reference to `PrintDebugString'
/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL/AccessBridgeCalls.c:74:(.text+0x93): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `PrintDebugString'
/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL/AccessBridgeCalls.c:76: undefined reference to `PrintDebugString'
/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL/AccessBridgeCalls.c:76:(.text+0xb0): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `PrintDebugString'
/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL/AccessBridgeCalls.c:76: undefined reference to `PrintDebugString'
/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL/AccessBridgeCalls.c:76:(.text+0xfe): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `PrintDebugString'
/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL/AccessBridgeCalls.c:77: undefined reference to `PrintDebugString'
/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL/AccessBridgeCalls.c:77:(.text+0x11b): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `PrintDebugString'
build/Debug/Cygwin-Windows/AccessBridgeCalls.o:/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL/AccessBridgeCalls.c:77: more undefined references to `PrintDebugString' follow
build/Debug/Cygwin-Windows/AccessBridgeCalls.o: In function `initializeAccessBridge':
/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL/AccessBridgeCalls.c:77:(.text+0x169): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `PrintDebugString'
/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL/AccessBridgeCalls.c:78:(.text+0x186): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `PrintDebugString'
/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL/AccessBridgeCalls.c:78:(.text+0x1d4): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `PrintDebugString'
/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL/AccessBridgeCalls.c:80:(.text+0x1f1): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `PrintDebugString'
/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL/AccessBridgeCalls.c:80:(.text+0x23f): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `PrintDebugString'
/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL/AccessBridgeCalls.c:82:(.text+0x25c): additional relocation overflows omitted from the output
collect2: error: ld returned 1 exit status
make[2]: *** [nbproject/Makefile-Debug.mk:63: dist/Debug/Cygwin-Windows/libJniDLL.dll] Error 1
make[2]: Leaving directory '/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL'
make[1]: *** [nbproject/Makefile-Debug.mk:59: .build-conf] Error 2
make[1]: Leaving directory '/cygdrive/c/Users/mogel/Documents/NetBeansProjects/JniDLL'
make: *** [nbproject/Makefile-impl.mk:40: .build-impl] Error 2

BUILD FAILED (exit value 2, total time: 1s)

Ich muss jetzt erst mal Mittag machen. Noch sind die Raubtiere beim Spielen von Robocraft abgelenk. Das wird aber nicht mehr lange anhalten ^^

cd '/home/mogel/NetBeansProjects/JniDLL'
/usr/bin/make -f Makefile CONF=Debug
"/usr/bin/make" -f nbproject/Makefile-Debug.mk QMAKE= SUBPROJECTS= .build-conf
make[1]: Verzeichnis „/home/mogel/NetBeansProjects/JniDLL“ wird betreten
"/usr/bin/make"  -f nbproject/Makefile-Debug.mk dist/Debug/GNU-Linux/libJniDLL.so
make[2]: Verzeichnis „/home/mogel/NetBeansProjects/JniDLL“ wird betreten
mkdir -p build/Debug/GNU-Linux
rm -f "build/Debug/GNU-Linux/main.o.d"
gcc    -c -g -I/opt/java/current/include -I/opt/java/current/include/linux -fPIC  -MMD -MP -MF "build/Debug/GNU-Linux/main.o.d" -o build/Debug/GNU-Linux/main.o main.c
mkdir -p dist/Debug/GNU-Linux
gcc     -o dist/Debug/GNU-Linux/libJniDLL.so build/Debug/GNU-Linux/main.o  -shared -fPIC
make[2]: Verzeichnis „/home/mogel/NetBeansProjects/JniDLL“ wird verlassen
make[1]: Verzeichnis „/home/mogel/NetBeansProjects/JniDLL“ wird verlassen

BUILD SUCCESSFUL (total time: 151ms)

ick schaue nochmal unter Windows

Is schonmal ein guter Fortschritt wenns unter Linux klappt! Bin bis jetz noch ncih so weiter gekommen, viel Erfolg

Soooo,

einige Änderungen:

main.c:1:9: warning: #pragma once in main file
 #pragma once
         ^
In file included from /cygdrive/C/Program Files/Java/jdk1.8.0_101/include/jni.h:45:0,
                 from jniHeader.h:2,
                 from main.c:3:

Das #pragma once ist nur für Header-Files. Das verhindert das ein Header-File doppelt eingelesen wird und dann der Compiler meckert (weil ja diverse Definitionen schon bekannt sind und dann auf einmal doppelt existieren).


main.c: In function 'Java_JNICallTest_CLibSaysHello':
main.c:6:1: error: parameter name omitted
 JNIEXPORT void JNICALL Java_JNICallTest_CLibSaysHello(JNIEnv*,  jclass) {

Man lässt den Namen der Variablen nicht einfach weg - das ist alter Style.

Java_JNICallTest_CLibSaysHello(JNIEnv* temp1,  jclass temp2)

Um den Patch für Windows und das __int64 kommst Du nicht herum (stand irgendwo in dem von Dir gepostet Tutorial).

#ifdef __GNUC__
    typedef long long jlong;
  #else
    typedef __int64 jlong;
#endif

Persönlich bin ich kein Fan davon in gelieferten Codes etwas zu ändern (das muss nach jedem Update geändert werden). Aber hier kommt eben zum dieses Hybrid-Dingens (Windows->Hybrid-Linux->Windows-DLL) zum tragen. Unter Linux (als Linux-Library) musste ich das nicht machen.


Nun noch einige Änderungen an den Projekt-Settings

In den Zweig “Header Files” gehören nur Header-Files rein. (Da war auch die AccesBridge.c [oder wie auch immer das Ding hieß] mit drinnen). Ich habe da alles rausgeschmissen und die “jniHeader.h” reingezogen.


Das Problem des leeren Projekts, lag einfach daran das Deine “main.c” mit dem Custombuild-Tool gebaut werden sollte. Ich habe des in der Properties auf “C Compiler” umgestellt.


Und dann noch die Include-Dateien wie schon gestern beschrieben hinzufügen. Also nur die Verzeichnisse und die Pfade absolut.

  • -I/cygdrive/C/Program\ Files/Java/jdk1.8.0_101/include
  • -I../../../../../../Program\ Files/Java/jdk1.8.0_101/include

Bei Letzterem findet er nämlich die Verzeichnisse nicht. Cygwin geht an der Stelle immer nach oben und landet irgend wann im Root-Verzeichnis. Dort sucht das dann nach “Program Files”, das aber nicht existiert. Das fehlt nämlich der Laufwerksbuchstabe. Mit den absoluten Pfaden findet er dann aber die Include-Verzeichnisse (Hybrid-Dingens-Problem).


Denke daran die Pfad noch richtig zu setzen, ich habe eine etwas andere Java-Version. Außerdem habe ich NetBeans 8.2 verwendet. JniDLL.zip (64,0 KB)

Danke es funktioniert! Allerdings muss ich unter WIndows ja noch den Linker-Flag einstellen, da java die DLL sonst nicht erkennt. Bekomm so immer den UnsatisfiedLinkError. Ich probier mal bissl rum, evtl krieg ichs hin.

Edit: Also ich hab in den Vm-Options den Library path gesetzt, wenn ich die Java-Datei starten will kommt allerdings das:

Also iwie stimmt da noch was nich. Ich denke es liegt an den Linker options aber wenn die ich die in die additional options einfüge passiert einfach nix

Spiel Mal mit den 32Bit und 64Bit Optionen rum. Wenn du 64Bit Java verwendest, kannst du keine 32Bit DLL laden.

Es is auf 64bit eingestellt… Hmmm

static {
    int sglibLoadError = 0;

    if(System.getProperty("os.name").toLowerCase().indexOf("win") >= 0)
        try{
            System.loadLibrary("SglW32");
        }
        catch(UnsatisfiedLinkError e){
            try{
                System.load(System.getProperty("user.dir") + "/sglw32.dll");
            }
            catch(UnsatisfiedLinkError b){
                sglibLoadError = 1;
                System.out.println("Library not found");
            }
            if(sglibLoadError == 0)
                System.out.println("Loading library from local dir");
        }

    if(System.getProperty("os.name").toLowerCase().indexOf("nux") >= 0)
        try{
            System.loadLibrary("sgllnx");
        }
        catch(UnsatisfiedLinkError e){
            try{
                System.load(System.getProperty("user.dir") + "/libsgllnx.so");
            }
            catch(UnsatisfiedLinkError b){
                sglibLoadError = 1;
                System.out.println("Libary not found");
            }
            if(sglibLoadError == 0)
                System.out.println("Loading library from local dir");
        }

    if(System.getProperty("os.name").toLowerCase().indexOf("mac") >= 0)
        try{
            System.loadLibrary("sglmac");
        }
        catch(UnsatisfiedLinkError e){
            try{
                System.load(System.getProperty("user.dir") + "/libsglmac.dylib");
            }
            catch(UnsatisfiedLinkError b){
                System.out.println("Library not found");
            }
            if(sglibLoadError == 0)
                System.out.println("Loading library from local dir");
        }
}

Sortiere Dir das mal ein bisschen und versuche es mal damit.

Also ich hab die Pfadnamen in die meiner dll geändert und Bekomme als Ausgabe “Library not found” obwohl die im gleichen Ordner wie die -java liegt. Aber ich denke auch das Problem ist eher das er die dependent Libraries nicht findet

Ich glaube GCC linkt da zusätzlich dynamisch gegen eine eigene DLL. Entweder umstellen auf statisch (weis nicht ob es geht) oder die DLL finden ->z.B. mit http://www.dependencywalker.com/

Ich hab das Projekt an dem wir seit gestern rummachen halt als dynamische Library erstellt. Hätte es auch statisch erstellen können, wäre wohl besser gewesen? Hm. Ich versuchs mal

nein - weil der GCC wieder dynamisch gegen seine eigene DLL link. Wird kein Unterschied sein

By default cygwin gcc produces binaries linked against cygwin.dll.
http://stackoverflow.com/questions/4143629/executable-file-generated-using-gcc-under-cygwin

irgend wo auf der Seite gefunden